summaryrefslogtreecommitdiff
path: root/src/ai
diff options
context:
space:
mode:
authortruebrain <truebrain@openttd.org>2009-01-12 17:11:45 +0000
committertruebrain <truebrain@openttd.org>2009-01-12 17:11:45 +0000
commita3dd7506d377b1434f913bd65c019eed52b64b6e (patch)
treeced1a262eb143ad6e64ec02f4a4c89835c0c32fd /src/ai
parent9294f9616866b9778c22076c19b5a32b4f85f788 (diff)
downloadopenttd-a3dd7506d377b1434f913bd65c019eed52b64b6e.tar.xz
(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/hacks
Diffstat (limited to 'src/ai')
-rw-r--r--src/ai/ai.cpp244
-rw-r--r--src/ai/ai.h115
-rw-r--r--src/ai/ai.hpp102
-rw-r--r--src/ai/ai_config.cpp143
-rw-r--r--src/ai/ai_config.hpp91
-rw-r--r--src/ai/ai_core.cpp254
-rw-r--r--src/ai/ai_gui.cpp257
-rw-r--r--src/ai/ai_gui.hpp10
-rw-r--r--src/ai/ai_info.cpp319
-rw-r--r--src/ai/ai_info.hpp153
-rw-r--r--src/ai/ai_info_dummy.cpp66
-rw-r--r--src/ai/ai_instance.cpp628
-rw-r--r--src/ai/ai_instance.hpp140
-rw-r--r--src/ai/ai_scanner.cpp395
-rw-r--r--src/ai/ai_scanner.hpp102
-rw-r--r--src/ai/ai_storage.hpp78
-rw-r--r--src/ai/api/Doxyfile244
-rw-r--r--src/ai/api/ai_abstractlist.cpp818
-rw-r--r--src/ai/api/ai_abstractlist.hpp263
-rw-r--r--src/ai/api/ai_abstractlist.hpp.sq59
-rw-r--r--src/ai/api/ai_accounting.cpp26
-rw-r--r--src/ai/api/ai_accounting.hpp54
-rw-r--r--src/ai/api/ai_accounting.hpp.sq26
-rw-r--r--src/ai/api/ai_airport.cpp130
-rw-r--r--src/ai/api/ai_airport.hpp164
-rw-r--r--src/ai/api/ai_airport.hpp.sq57
-rw-r--r--src/ai/api/ai_base.cpp43
-rw-r--r--src/ai/api/ai_base.hpp71
-rw-r--r--src/ai/api/ai_base.hpp.sq29
-rw-r--r--src/ai/api/ai_bridge.cpp177
-rw-r--r--src/ai/api/ai_bridge.hpp164
-rw-r--r--src/ai/api/ai_bridge.hpp.sq51
-rw-r--r--src/ai/api/ai_bridgelist.cpp25
-rw-r--r--src/ai/api/ai_bridgelist.hpp34
-rw-r--r--src/ai/api/ai_bridgelist.hpp.sq42
-rw-r--r--src/ai/api/ai_cargo.cpp57
-rw-r--r--src/ai/api/ai_cargo.hpp94
-rw-r--r--src/ai/api/ai_cargo.hpp.sq52
-rw-r--r--src/ai/api/ai_cargolist.cpp46
-rw-r--r--src/ai/api/ai_cargolist.hpp48
-rw-r--r--src/ai/api/ai_cargolist.hpp.sq61
-rw-r--r--src/ai/api/ai_company.cpp200
-rw-r--r--src/ai/api/ai_company.hpp214
-rw-r--r--src/ai/api/ai_company.hpp.sq55
-rw-r--r--src/ai/api/ai_controller.cpp85
-rw-r--r--src/ai/api/ai_controller.hpp117
-rw-r--r--src/ai/api/ai_controller.hpp.sq12
-rw-r--r--src/ai/api/ai_date.cpp47
-rw-r--r--src/ai/api/ai_date.hpp64
-rw-r--r--src/ai/api/ai_date.hpp.sq28
-rw-r--r--src/ai/api/ai_depotlist.cpp42
-rw-r--r--src/ai/api/ai_depotlist.hpp25
-rw-r--r--src/ai/api/ai_depotlist.hpp.sq23
-rw-r--r--src/ai/api/ai_engine.cpp295
-rw-r--r--src/ai/api/ai_engine.hpp202
-rw-r--r--src/ai/api/ai_engine.hpp.sq42
-rw-r--r--src/ai/api/ai_enginelist.cpp16
-rw-r--r--src/ai/api/ai_enginelist.hpp25
-rw-r--r--src/ai/api/ai_enginelist.hpp.sq23
-rw-r--r--src/ai/api/ai_error.cpp60
-rw-r--r--src/ai/api/ai_error.hpp171
-rw-r--r--src/ai/api/ai_error.hpp.sq121
-rw-r--r--src/ai/api/ai_event.cpp65
-rw-r--r--src/ai/api/ai_event.hpp107
-rw-r--r--src/ai/api/ai_event.hpp.sq72
-rw-r--r--src/ai/api/ai_event_types.cpp187
-rw-r--r--src/ai/api/ai_event_types.hpp684
-rw-r--r--src/ai/api/ai_event_types.hpp.sq393
-rw-r--r--src/ai/api/ai_execmode.cpp26
-rw-r--r--src/ai/api/ai_execmode.hpp46
-rw-r--r--src/ai/api/ai_execmode.hpp.sq23
-rw-r--r--src/ai/api/ai_gamesettings.cpp38
-rw-r--r--src/ai/api/ai_gamesettings.hpp67
-rw-r--r--src/ai/api/ai_gamesettings.hpp.sq26
-rw-r--r--src/ai/api/ai_group.cpp128
-rw-r--r--src/ai/api/ai_group.hpp170
-rw-r--r--src/ai/api/ai_group.hpp.sq46
-rw-r--r--src/ai/api/ai_grouplist.cpp16
-rw-r--r--src/ai/api/ai_grouplist.hpp21
-rw-r--r--src/ai/api/ai_grouplist.hpp.sq23
-rw-r--r--src/ai/api/ai_industry.cpp170
-rw-r--r--src/ai/api/ai_industry.hpp171
-rw-r--r--src/ai/api/ai_industry.hpp.sq40
-rw-r--r--src/ai/api/ai_industrylist.cpp42
-rw-r--r--src/ai/api/ai_industrylist.hpp49
-rw-r--r--src/ai/api/ai_industrylist.hpp.sq61
-rw-r--r--src/ai/api/ai_industrytype.cpp115
-rw-r--r--src/ai/api/ai_industrytype.hpp114
-rw-r--r--src/ai/api/ai_industrytype.hpp.sq34
-rw-r--r--src/ai/api/ai_industrytypelist.cpp15
-rw-r--r--src/ai/api/ai_industrytypelist.hpp22
-rw-r--r--src/ai/api/ai_industrytypelist.hpp.sq23
-rw-r--r--src/ai/api/ai_list.cpp45
-rw-r--r--src/ai/api/ai_list.hpp47
-rw-r--r--src/ai/api/ai_list.hpp.sq28
-rw-r--r--src/ai/api/ai_log.cpp84
-rw-r--r--src/ai/api/ai_log.hpp76
-rw-r--r--src/ai/api/ai_log.hpp.sq37
-rw-r--r--src/ai/api/ai_map.cpp62
-rw-r--r--src/ai/api/ai_map.hpp117
-rw-r--r--src/ai/api/ai_map.hpp.sq34
-rw-r--r--src/ai/api/ai_marine.cpp143
-rw-r--r--src/ai/api/ai_marine.hpp187
-rw-r--r--src/ai/api/ai_marine.hpp.sq50
-rw-r--r--src/ai/api/ai_object.cpp264
-rw-r--r--src/ai/api/ai_object.hpp202
-rw-r--r--src/ai/api/ai_order.cpp296
-rw-r--r--src/ai/api/ai_order.hpp253
-rw-r--r--src/ai/api/ai_order.hpp.sq71
-rw-r--r--src/ai/api/ai_rail.cpp442
-rw-r--r--src/ai/api/ai_rail.hpp390
-rw-r--r--src/ai/api/ai_rail.hpp.sq93
-rw-r--r--src/ai/api/ai_railtypelist.cpp14
-rw-r--r--src/ai/api/ai_railtypelist.hpp20
-rw-r--r--src/ai/api/ai_railtypelist.hpp.sq23
-rw-r--r--src/ai/api/ai_road.cpp548
-rw-r--r--src/ai/api/ai_road.hpp408
-rw-r--r--src/ai/api/ai_road.hpp.sq73
-rw-r--r--src/ai/api/ai_sign.cpp73
-rw-r--r--src/ai/api/ai_sign.hpp96
-rw-r--r--src/ai/api/ai_sign.hpp.sq41
-rw-r--r--src/ai/api/ai_station.cpp146
-rw-r--r--src/ai/api/ai_station.hpp184
-rw-r--r--src/ai/api/ai_station.hpp.sq69
-rw-r--r--src/ai/api/ai_stationlist.cpp29
-rw-r--r--src/ai/api/ai_stationlist.hpp39
-rw-r--r--src/ai/api/ai_stationlist.hpp.sq42
-rw-r--r--src/ai/api/ai_subsidy.cpp94
-rw-r--r--src/ai/api/ai_subsidy.hpp101
-rw-r--r--src/ai/api/ai_subsidy.hpp.sq32
-rw-r--r--src/ai/api/ai_subsidylist.cpp14
-rw-r--r--src/ai/api/ai_subsidylist.hpp20
-rw-r--r--src/ai/api/ai_subsidylist.hpp.sq23
-rw-r--r--src/ai/api/ai_testmode.cpp26
-rw-r--r--src/ai/api/ai_testmode.hpp48
-rw-r--r--src/ai/api/ai_testmode.hpp.sq23
-rw-r--r--src/ai/api/ai_tile.cpp239
-rw-r--r--src/ai/api/ai_tile.hpp362
-rw-r--r--src/ai/api/ai_tile.hpp.sq97
-rw-r--r--src/ai/api/ai_tilelist.cpp175
-rw-r--r--src/ai/api/ai_tilelist.hpp110
-rw-r--r--src/ai/api/ai_tilelist.hpp.sq85
-rw-r--r--src/ai/api/ai_town.cpp197
-rw-r--r--src/ai/api/ai_town.hpp284
-rw-r--r--src/ai/api/ai_town.hpp.sq69
-rw-r--r--src/ai/api/ai_townlist.cpp15
-rw-r--r--src/ai/api/ai_townlist.hpp20
-rw-r--r--src/ai/api/ai_townlist.hpp.sq23
-rw-r--r--src/ai/api/ai_tunnel.cpp119
-rw-r--r--src/ai/api/ai_tunnel.hpp103
-rw-r--r--src/ai/api/ai_tunnel.hpp.sq47
-rw-r--r--src/ai/api/ai_types.hpp40
-rw-r--r--src/ai/api/ai_vehicle.cpp389
-rw-r--r--src/ai/api/ai_vehicle.hpp477
-rw-r--r--src/ai/api/ai_vehicle.hpp.sq141
-rw-r--r--src/ai/api/ai_vehiclelist.cpp37
-rw-r--r--src/ai/api/ai_vehiclelist.hpp34
-rw-r--r--src/ai/api/ai_vehiclelist.hpp.sq42
-rw-r--r--src/ai/api/squirrel_export.awk385
-rw-r--r--src/ai/api/squirrel_export.sh93
-rw-r--r--src/ai/default/default.cpp4028
-rw-r--r--src/ai/default/default.h69
-rw-r--r--src/ai/trolly/build.cpp328
-rw-r--r--src/ai/trolly/pathfinder.cpp482
-rw-r--r--src/ai/trolly/shared.cpp121
-rw-r--r--src/ai/trolly/trolly.cpp1348
-rw-r--r--src/ai/trolly/trolly.h344
167 files changed, 19124 insertions, 7079 deletions
diff --git a/src/ai/ai.cpp b/src/ai/ai.cpp
deleted file mode 100644
index 01c8dcd68..000000000
--- a/src/ai/ai.cpp
+++ /dev/null
@@ -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);
-}
diff --git a/src/ai/ai.h b/src/ai/ai.h
deleted file mode 100644
index 0f18490d5..000000000
--- a/src/ai/ai.h
+++ /dev/null
@@ -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 */
diff --git a/src/ai/ai.hpp b/src/ai/ai.hpp
new file mode 100644
index 000000000..bd1c2a907
--- /dev/null
+++ b/src/ai/ai.hpp
@@ -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 */
diff --git a/src/ai/ai_config.cpp b/src/ai/ai_config.cpp
new file mode 100644
index 000000000..f5d22bdaa
--- /dev/null
+++ b/src/ai/ai_config.cpp
@@ -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';
+}
diff --git a/src/ai/ai_config.hpp b/src/ai/ai_config.hpp
new file mode 100644
index 000000000..c628a855f
--- /dev/null
+++ b/src/ai/ai_config.hpp
@@ -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 */
diff --git a/src/ai/ai_core.cpp b/src/ai/ai_core.cpp
new file mode 100644
index 000000000..f0a82f771
--- /dev/null
+++ b/src/ai/ai_core.cpp
@@ -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();
+}
diff --git a/src/ai/ai_gui.cpp b/src/ai/ai_gui.cpp
new file mode 100644
index 000000000..8272cc966
--- /dev/null
+++ b/src/ai/ai_gui.cpp
@@ -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);
+ }
+}
diff --git a/src/ai/ai_gui.hpp b/src/ai/ai_gui.hpp
new file mode 100644
index 000000000..c5c7ad2ee
--- /dev/null
+++ b/src/ai/ai_gui.hpp
@@ -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 */
diff --git a/src/ai/ai_info.cpp b/src/ai/ai_info.cpp
new file mode 100644
index 000000000..0eead0302
--- /dev/null
+++ b/src/ai/ai_info.cpp
@@ -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;
+}
diff --git a/src/ai/ai_info.hpp b/src/ai/ai_info.hpp
new file mode 100644
index 000000000..d50b89584
--- /dev/null
+++ b/src/ai/ai_info.hpp
@@ -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 */
diff --git a/src/ai/ai_info_dummy.cpp b/src/ai/ai_info_dummy.cpp
new file mode 100644
index 000000000..a060a0a65
--- /dev/null
+++ b/src/ai/ai_info_dummy.cpp
@@ -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();
+}
diff --git a/src/ai/ai_instance.cpp b/src/ai/ai_instance.cpp
new file mode 100644
index 000000000..a0612498d
--- /dev/null
+++ b/src/ai/ai_instance.cpp
@@ -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;
+}
diff --git a/src/ai/ai_instance.hpp b/src/ai/ai_instance.hpp
new file mode 100644
index 000000000..d953c3915
--- /dev/null
+++ b/src/ai/ai_instance.hpp
@@ -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 */
diff --git a/src/ai/ai_scanner.cpp b/src/ai/ai_scanner.cpp
new file mode 100644
index 000000000..68d53660d
--- /dev/null
+++ b/src/ai/ai_scanner.cpp
@@ -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;
+}
diff --git a/src/ai/ai_scanner.hpp b/src/ai/ai_scanner.hpp
new file mode 100644
index 000000000..2801f556a
--- /dev/null
+++ b/src/ai/ai_scanner.hpp
@@ -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 */
diff --git a/src/ai/ai_storage.hpp b/src/ai/ai_storage.hpp
new file mode 100644
index 000000000..2e85046b4
--- /dev/null
+++ b/src/ai/ai_storage.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 */
diff --git a/src/ai/api/Doxyfile b/src/ai/api/Doxyfile
new file mode 100644
index 000000000..ff5d7a511
--- /dev/null
+++ b/src/ai/api/Doxyfile
@@ -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
diff --git a/src/ai/api/ai_abstractlist.cpp b/src/ai/api/ai_abstractlist.cpp
new file mode 100644
index 000000000..d447311ac
--- /dev/null
+++ b/src/ai/api/ai_abstractlist.cpp
@@ -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;
+}
diff --git a/src/ai/api/ai_abstractlist.hpp b/src/ai/api/ai_abstractlist.hpp
new file mode 100644
index 000000000..55a4559f8
--- /dev/null
+++ b/src/ai/api/ai_abstractlist.hpp
@@ -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 */
diff --git a/src/ai/api/ai_abstractlist.hpp.sq b/src/ai/api/ai_abstractlist.hpp.sq
new file mode 100644
index 000000000..25d9f3904
--- /dev/null
+++ b/src/ai/api/ai_abstractlist.hpp.sq
@@ -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);
+}
diff --git a/src/ai/api/ai_accounting.cpp b/src/ai/api/ai_accounting.cpp
new file mode 100644
index 000000000..2134d39b0
--- /dev/null
+++ b/src/ai/api/ai_accounting.cpp
@@ -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);
+}
diff --git a/src/ai/api/ai_accounting.hpp b/src/ai/api/ai_accounting.hpp
new file mode 100644
index 000000000..bffa051f1
--- /dev/null
+++ b/src/ai/api/ai_accounting.hpp
@@ -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 */
diff --git a/src/ai/api/ai_accounting.hpp.sq b/src/ai/api/ai_accounting.hpp.sq
new file mode 100644
index 000000000..18922b24c
--- /dev/null
+++ b/src/ai/api/ai_accounting.hpp.sq
@@ -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);
+}
diff --git a/src/ai/api/ai_airport.cpp b/src/ai/api/ai_airport.cpp
new file mode 100644
index 000000000..3e435d7bc
--- /dev/null
+++ b/src/ai/api/ai_airport.cpp
@@ -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;
+}
diff --git a/src/ai/api/ai_airport.hpp b/src/ai/api/ai_airport.hpp
new file mode 100644
index 000000000..d1d18714b
--- /dev/null
+++ b/src/ai/api/ai_airport.hpp
@@ -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 */
diff --git a/src/ai/api/ai_airport.hpp.sq b/src/ai/api/ai_airport.hpp.sq
new file mode 100644
index 000000000..355a2ab12
--- /dev/null
+++ b/src/ai/api/ai_airport.hpp.sq
@@ -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);
+}
diff --git a/src/ai/api/ai_base.cpp b/src/ai/api/ai_base.cpp
new file mode 100644
index 000000000..fa04bac36
--- /dev/null
+++ b/src/ai/api/ai_base.cpp
@@ -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);
+}
diff --git a/src/ai/api/ai_base.hpp b/src/ai/api/ai_base.hpp
new file mode 100644
index 000000000..9a3aad742
--- /dev/null
+++ b/src/ai/api/ai_base.hpp
@@ -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 */
diff --git a/src/ai/api/ai_base.hpp.sq b/src/ai/api/ai_base.hpp.sq
new file mode 100644
index 000000000..577301936
--- /dev/null
+++ b/src/ai/api/ai_base.hpp.sq
@@ -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);
+}
diff --git a/src/ai/api/ai_bridge.cpp b/src/ai/api/ai_bridge.cpp
new file mode 100644
index 000000000..86eb5f6f1
--- /dev/null
+++ b/src/ai/api/ai_bridge.cpp
@@ -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);
+}
diff --git a/src/ai/api/ai_bridge.hpp b/src/ai/api/ai_bridge.hpp
new file mode 100644
index 000000000..7bd637277
--- /dev/null
+++ b/src/ai/api/ai_bridge.hpp
@@ -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 */
diff --git a/src/ai/api/ai_bridge.hpp.sq b/src/ai/api/ai_bridge.hpp.sq
new file mode 100644
index 000000000..6388d82cf
--- /dev/null
+++ b/src/ai/api/ai_bridge.hpp.sq
@@ -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);
+}
diff --git a/src/ai/api/ai_bridgelist.cpp b/src/ai/api/ai_bridgelist.cpp
new file mode 100644
index 000000000..4f2fb2719
--- /dev/null
+++ b/src/ai/api/ai_bridgelist.cpp
@@ -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);
+}
diff --git a/src/ai/api/ai_bridgelist.hpp b/src/ai/api/ai_bridgelist.hpp
new file mode 100644
index 000000000..b3ba39058
--- /dev/null
+++ b/src/ai/api/ai_bridgelist.hpp
@@ -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 */
diff --git a/src/ai/api/ai_bridgelist.hpp.sq b/src/ai/api/ai_bridgelist.hpp.sq
new file mode 100644
index 000000000..f34b95518
--- /dev/null
+++ b/src/ai/api/ai_bridgelist.hpp.sq
@@ -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);
+}
diff --git a/src/ai/api/ai_cargo.cpp b/src/ai/api/ai_cargo.cpp
new file mode 100644
index 000000000..6843f7134
--- /dev/null
+++ b/src/ai/api/ai_cargo.cpp
@@ -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);
+}
diff --git a/src/ai/api/ai_cargo.hpp b/src/ai/api/ai_cargo.hpp
new file mode 100644
index 000000000..bde235758
--- /dev/null
+++ b/src/ai/api/ai_cargo.hpp
@@ -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 */
diff --git a/src/ai/api/ai_cargo.hpp.sq b/src/ai/api/ai_cargo.hpp.sq
new file mode 100644
index 000000000..015d112b7
--- /dev/null
+++ b/src/ai/api/ai_cargo.hpp.sq
@@ -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);
+}
diff --git a/src/ai/api/ai_cargolist.cpp b/src/ai/api/ai_cargolist.cpp
new file mode 100644
index 000000000..e480895fc
--- /dev/null
+++ b/src/ai/api/ai_cargolist.cpp
@@ -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);
+ }
+ }
+}
diff --git a/src/ai/api/ai_cargolist.hpp b/src/ai/api/ai_cargolist.hpp
new file mode 100644
index 000000000..86a99a393
--- /dev/null
+++ b/src/ai/api/ai_cargolist.hpp
@@ -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 */
diff --git a/src/ai/api/ai_cargolist.hpp.sq b/src/ai/api/ai_cargolist.hpp.sq
new file mode 100644
index 000000000..379afed9b
--- /dev/null
+++ b/src/ai/api/ai_cargolist.hpp.sq
@@ -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);
+}
diff --git a/src/ai/api/ai_company.cpp b/src/ai/api/ai_company.cpp
new file mode 100644
index 000000000..9a426bd2b
--- /dev/null
+++ b/src/ai/api/ai_company.cpp
@@ -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;
+}
diff --git a/src/ai/api/ai_company.hpp b/src/ai/api/ai_company.hpp
new file mode 100644
index 000000000..2497e43b7
--- /dev/null
+++ b/src/ai/api/ai_company.hpp
@@ -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 */
diff --git a/src/ai/api/ai_company.hpp.sq b/src/ai/api/ai_company.hpp.sq
new file mode 100644
index 000000000..0f0bb5ccf
--- /dev/null
+++ b/src/ai/api/ai_company.hpp.sq
@@ -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);
+}
diff --git a/src/ai/api/ai_controller.cpp b/src/ai/api/ai_controller.cpp
new file mode 100644
index 000000000..cb1fb6819
--- /dev/null
+++ b/src/ai/api/ai_controller.cpp
@@ -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);
+}
diff --git a/src/ai/api/ai_controller.hpp b/src/ai/api/ai_controller.hpp
new file mode 100644
index 000000000..1f1df743e
--- /dev/null
+++ b/src/ai/api/ai_controller.hpp
@@ -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 */
diff --git a/src/ai/api/ai_controller.hpp.sq b/src/ai/api/ai_controller.hpp.sq
new file mode 100644
index 000000000..d72574be7
--- /dev/null
+++ b/src/ai/api/ai_controller.hpp.sq
@@ -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);
+}
diff --git a/src/ai/api/ai_date.cpp b/src/ai/api/ai_date.cpp
new file mode 100644
index 000000000..b3ad552c9
--- /dev/null
+++ b/src/ai/api/ai_date.cpp
@@ -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);
+}
diff --git a/src/ai/api/ai_date.hpp b/src/ai/api/ai_date.hpp
new file mode 100644
index 000000000..973b1b116
--- /dev/null
+++ b/src/ai/api/ai_date.hpp
@@ -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 */
diff --git a/src/ai/api/ai_date.hpp.sq b/src/ai/api/ai_date.hpp.sq
new file mode 100644
index 000000000..5e6d7797d
--- /dev/null
+++ b/src/ai/api/ai_date.hpp.sq
@@ -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);
+}
diff --git a/src/ai/api/ai_depotlist.cpp b/src/ai/api/ai_depotlist.cpp
new file mode 100644
index 000000000..b93ec3aef
--- /dev/null
+++ b/src/ai/api/ai_depotlist.cpp
@@ -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);
+ }
+}
diff --git a/src/ai/api/ai_depotlist.hpp b/src/ai/api/ai_depotlist.hpp
new file mode 100644
index 000000000..0715b45bb
--- /dev/null
+++ b/src/ai/api/ai_depotlist.hpp
@@ -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 */
diff --git a/src/ai/api/ai_depotlist.hpp.sq b/src/ai/api/ai_depotlist.hpp.sq
new file mode 100644
index 000000000..51f799490
--- /dev/null
+++ b/src/ai/api/ai_depotlist.hpp.sq
@@ -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);
+}
diff --git a/src/ai/api/ai_engine.cpp b/src/ai/api/ai_engine.cpp
new file mode 100644
index 000000000..5215c95f3
--- /dev/null
+++ b/src/ai/api/ai_engine.cpp
@@ -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;
+}
diff --git a/src/ai/api/ai_engine.hpp b/src/ai/api/ai_engine.hpp
new file mode 100644
index 000000000..67801107c
--- /dev/null
+++ b/src/ai/api/ai_engine.hpp
@@ -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 */
diff --git a/src/ai/api/ai_engine.hpp.sq b/src/ai/api/ai_engine.hpp.sq
new file mode 100644
index 000000000..067d3c65e
--- /dev/null
+++ b/src/ai/api/ai_engine.hpp.sq
@@ -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);
+}
diff --git a/src/ai/api/ai_enginelist.cpp b/src/ai/api/ai_enginelist.cpp
new file mode 100644
index 000000000..bd57025f6
--- /dev/null
+++ b/src/ai/api/ai_enginelist.cpp
@@ -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);
+ }
+}
diff --git a/src/ai/api/ai_enginelist.hpp b/src/ai/api/ai_enginelist.hpp
new file mode 100644
index 000000000..8c299caf3
--- /dev/null
+++ b/src/ai/api/ai_enginelist.hpp
@@ -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 */
diff --git a/src/ai/api/ai_enginelist.hpp.sq b/src/ai/api/ai_enginelist.hpp.sq
new file mode 100644
index 000000000..98054d22d
--- /dev/null
+++ b/src/ai/api/ai_enginelist.hpp.sq
@@ -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);
+}
diff --git a/src/ai/api/ai_error.cpp b/src/ai/api/ai_error.cpp
new file mode 100644
index 000000000..4c21db726
--- /dev/null
+++ b/src/ai/api/ai_error.cpp
@@ -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);
+}
diff --git a/src/ai/api/ai_error.hpp b/src/ai/api/ai_error.hpp
new file mode 100644
index 000000000..6a448ff3a
--- /dev/null
+++ b/src/ai/api/ai_error.hpp
@@ -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 */
diff --git a/src/ai/api/ai_error.hpp.sq b/src/ai/api/ai_error.hpp.sq
new file mode 100644
index 000000000..1955de76b
--- /dev/null
+++ b/src/ai/api/ai_error.hpp.sq
@@ -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);
+}
diff --git a/src/ai/api/ai_event.cpp b/src/ai/api/ai_event.cpp
new file mode 100644
index 000000000..b18fa4777
--- /dev/null
+++ b/src/ai/api/ai_event.cpp
@@ -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);
+}
+
diff --git a/src/ai/api/ai_event.hpp b/src/ai/api/ai_event.hpp
new file mode 100644
index 000000000..a1304ec33
--- /dev/null
+++ b/src/ai/api/ai_event.hpp
@@ -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 */
diff --git a/src/ai/api/ai_event.hpp.sq b/src/ai/api/ai_event.hpp.sq
new file mode 100644
index 000000000..f5ac02203
--- /dev/null
+++ b/src/ai/api/ai_event.hpp.sq
@@ -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);
+}
diff --git a/src/ai/api/ai_event_types.cpp b/src/ai/api/ai_event_types.cpp
new file mode 100644
index 000000000..07b324cfa
--- /dev/null
+++ b/src/ai/api/ai_event_types.cpp
@@ -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);
+}
diff --git a/src/ai/api/ai_event_types.hpp b/src/ai/api/ai_event_types.hpp
new file mode 100644
index 000000000..c64b2a367
--- /dev/null
+++ b/src/ai/api/ai_event_types.hpp
@@ -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 */
diff --git a/src/ai/api/ai_event_types.hpp.sq b/src/ai/api/ai_event_types.hpp.sq
new file mode 100644
index 000000000..84d8b722b
--- /dev/null
+++ b/src/ai/api/ai_event_types.hpp.sq
@@ -0,0 +1,393 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_event_types.hpp"
+
+namespace SQConvert {
+ /* Allow AIEventTest to be used as Squirrel parameter */
+ template <> AIEventTest *GetParam(ForceType<AIEventTest *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventTest *)instance; }
+ template <> AIEventTest &GetParam(ForceType<AIEventTest &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventTest *)instance; }
+ template <> const AIEventTest *GetParam(ForceType<const AIEventTest *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventTest *)instance; }
+ template <> const AIEventTest &GetParam(ForceType<const AIEventTest &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventTest *)instance; }
+ template <> int Return<AIEventTest *>(HSQUIRRELVM vm, AIEventTest *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventTest", res, NULL, DefSQDestructorCallback<AIEventTest>); return 1; }
+}; // namespace SQConvert
+
+void SQAIEventTest_Register(Squirrel *engine) {
+ DefSQClass <AIEventTest> SQAIEventTest("AIEventTest");
+ SQAIEventTest.PreRegister(engine, "AIEvent");
+
+ SQAIEventTest.DefSQStaticMethod(engine, &AIEventTest::GetClassName, "GetClassName", 1, "x");
+ SQAIEventTest.DefSQStaticMethod(engine, &AIEventTest::Convert, "Convert", 2, "xx");
+
+ SQAIEventTest.DefSQMethod(engine, &AIEventTest::GetTest, "GetTest", 1, "x");
+
+ SQAIEventTest.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AIEventVehicleCrashed to be used as Squirrel parameter */
+ template <> AIEventVehicleCrashed *GetParam(ForceType<AIEventVehicleCrashed *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventVehicleCrashed *)instance; }
+ template <> AIEventVehicleCrashed &GetParam(ForceType<AIEventVehicleCrashed &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventVehicleCrashed *)instance; }
+ template <> const AIEventVehicleCrashed *GetParam(ForceType<const AIEventVehicleCrashed *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventVehicleCrashed *)instance; }
+ template <> const AIEventVehicleCrashed &GetParam(ForceType<const AIEventVehicleCrashed &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventVehicleCrashed *)instance; }
+ template <> int Return<AIEventVehicleCrashed *>(HSQUIRRELVM vm, AIEventVehicleCrashed *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventVehicleCrashed", res, NULL, DefSQDestructorCallback<AIEventVehicleCrashed>); return 1; }
+}; // namespace SQConvert
+
+void SQAIEventVehicleCrashed_Register(Squirrel *engine) {
+ DefSQClass <AIEventVehicleCrashed> SQAIEventVehicleCrashed("AIEventVehicleCrashed");
+ SQAIEventVehicleCrashed.PreRegister(engine, "AIEvent");
+
+ SQAIEventVehicleCrashed.DefSQStaticMethod(engine, &AIEventVehicleCrashed::GetClassName, "GetClassName", 1, "x");
+ SQAIEventVehicleCrashed.DefSQStaticMethod(engine, &AIEventVehicleCrashed::Convert, "Convert", 2, "xx");
+
+ SQAIEventVehicleCrashed.DefSQMethod(engine, &AIEventVehicleCrashed::GetVehicleID, "GetVehicleID", 1, "x");
+ SQAIEventVehicleCrashed.DefSQMethod(engine, &AIEventVehicleCrashed::GetCrashSite, "GetCrashSite", 1, "x");
+ SQAIEventVehicleCrashed.DefSQMethod(engine, &AIEventVehicleCrashed::CloneCrashedVehicle, "CloneCrashedVehicle", 2, "xi");
+
+ SQAIEventVehicleCrashed.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AIEventSubsidyOffer to be used as Squirrel parameter */
+ template <> AIEventSubsidyOffer *GetParam(ForceType<AIEventSubsidyOffer *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventSubsidyOffer *)instance; }
+ template <> AIEventSubsidyOffer &GetParam(ForceType<AIEventSubsidyOffer &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventSubsidyOffer *)instance; }
+ template <> const AIEventSubsidyOffer *GetParam(ForceType<const AIEventSubsidyOffer *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventSubsidyOffer *)instance; }
+ template <> const AIEventSubsidyOffer &GetParam(ForceType<const AIEventSubsidyOffer &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventSubsidyOffer *)instance; }
+ template <> int Return<AIEventSubsidyOffer *>(HSQUIRRELVM vm, AIEventSubsidyOffer *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventSubsidyOffer", res, NULL, DefSQDestructorCallback<AIEventSubsidyOffer>); return 1; }
+}; // namespace SQConvert
+
+void SQAIEventSubsidyOffer_Register(Squirrel *engine) {
+ DefSQClass <AIEventSubsidyOffer> SQAIEventSubsidyOffer("AIEventSubsidyOffer");
+ SQAIEventSubsidyOffer.PreRegister(engine, "AIEvent");
+
+ SQAIEventSubsidyOffer.DefSQStaticMethod(engine, &AIEventSubsidyOffer::GetClassName, "GetClassName", 1, "x");
+ SQAIEventSubsidyOffer.DefSQStaticMethod(engine, &AIEventSubsidyOffer::Convert, "Convert", 2, "xx");
+
+ SQAIEventSubsidyOffer.DefSQMethod(engine, &AIEventSubsidyOffer::GetSubsidyID, "GetSubsidyID", 1, "x");
+
+ SQAIEventSubsidyOffer.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AIEventSubsidyOfferExpired to be used as Squirrel parameter */
+ template <> AIEventSubsidyOfferExpired *GetParam(ForceType<AIEventSubsidyOfferExpired *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventSubsidyOfferExpired *)instance; }
+ template <> AIEventSubsidyOfferExpired &GetParam(ForceType<AIEventSubsidyOfferExpired &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventSubsidyOfferExpired *)instance; }
+ template <> const AIEventSubsidyOfferExpired *GetParam(ForceType<const AIEventSubsidyOfferExpired *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventSubsidyOfferExpired *)instance; }
+ template <> const AIEventSubsidyOfferExpired &GetParam(ForceType<const AIEventSubsidyOfferExpired &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventSubsidyOfferExpired *)instance; }
+ template <> int Return<AIEventSubsidyOfferExpired *>(HSQUIRRELVM vm, AIEventSubsidyOfferExpired *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventSubsidyOfferExpired", res, NULL, DefSQDestructorCallback<AIEventSubsidyOfferExpired>); return 1; }
+}; // namespace SQConvert
+
+void SQAIEventSubsidyOfferExpired_Register(Squirrel *engine) {
+ DefSQClass <AIEventSubsidyOfferExpired> SQAIEventSubsidyOfferExpired("AIEventSubsidyOfferExpired");
+ SQAIEventSubsidyOfferExpired.PreRegister(engine, "AIEvent");
+
+ SQAIEventSubsidyOfferExpired.DefSQStaticMethod(engine, &AIEventSubsidyOfferExpired::GetClassName, "GetClassName", 1, "x");
+ SQAIEventSubsidyOfferExpired.DefSQStaticMethod(engine, &AIEventSubsidyOfferExpired::Convert, "Convert", 2, "xx");
+
+ SQAIEventSubsidyOfferExpired.DefSQMethod(engine, &AIEventSubsidyOfferExpired::GetSubsidyID, "GetSubsidyID", 1, "x");
+
+ SQAIEventSubsidyOfferExpired.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AIEventSubsidyAwarded to be used as Squirrel parameter */
+ template <> AIEventSubsidyAwarded *GetParam(ForceType<AIEventSubsidyAwarded *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventSubsidyAwarded *)instance; }
+ template <> AIEventSubsidyAwarded &GetParam(ForceType<AIEventSubsidyAwarded &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventSubsidyAwarded *)instance; }
+ template <> const AIEventSubsidyAwarded *GetParam(ForceType<const AIEventSubsidyAwarded *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventSubsidyAwarded *)instance; }
+ template <> const AIEventSubsidyAwarded &GetParam(ForceType<const AIEventSubsidyAwarded &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventSubsidyAwarded *)instance; }
+ template <> int Return<AIEventSubsidyAwarded *>(HSQUIRRELVM vm, AIEventSubsidyAwarded *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventSubsidyAwarded", res, NULL, DefSQDestructorCallback<AIEventSubsidyAwarded>); return 1; }
+}; // namespace SQConvert
+
+void SQAIEventSubsidyAwarded_Register(Squirrel *engine) {
+ DefSQClass <AIEventSubsidyAwarded> SQAIEventSubsidyAwarded("AIEventSubsidyAwarded");
+ SQAIEventSubsidyAwarded.PreRegister(engine, "AIEvent");
+
+ SQAIEventSubsidyAwarded.DefSQStaticMethod(engine, &AIEventSubsidyAwarded::GetClassName, "GetClassName", 1, "x");
+ SQAIEventSubsidyAwarded.DefSQStaticMethod(engine, &AIEventSubsidyAwarded::Convert, "Convert", 2, "xx");
+
+ SQAIEventSubsidyAwarded.DefSQMethod(engine, &AIEventSubsidyAwarded::GetSubsidyID, "GetSubsidyID", 1, "x");
+
+ SQAIEventSubsidyAwarded.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AIEventSubsidyExpired to be used as Squirrel parameter */
+ template <> AIEventSubsidyExpired *GetParam(ForceType<AIEventSubsidyExpired *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventSubsidyExpired *)instance; }
+ template <> AIEventSubsidyExpired &GetParam(ForceType<AIEventSubsidyExpired &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventSubsidyExpired *)instance; }
+ template <> const AIEventSubsidyExpired *GetParam(ForceType<const AIEventSubsidyExpired *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventSubsidyExpired *)instance; }
+ template <> const AIEventSubsidyExpired &GetParam(ForceType<const AIEventSubsidyExpired &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventSubsidyExpired *)instance; }
+ template <> int Return<AIEventSubsidyExpired *>(HSQUIRRELVM vm, AIEventSubsidyExpired *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventSubsidyExpired", res, NULL, DefSQDestructorCallback<AIEventSubsidyExpired>); return 1; }
+}; // namespace SQConvert
+
+void SQAIEventSubsidyExpired_Register(Squirrel *engine) {
+ DefSQClass <AIEventSubsidyExpired> SQAIEventSubsidyExpired("AIEventSubsidyExpired");
+ SQAIEventSubsidyExpired.PreRegister(engine, "AIEvent");
+
+ SQAIEventSubsidyExpired.DefSQStaticMethod(engine, &AIEventSubsidyExpired::GetClassName, "GetClassName", 1, "x");
+ SQAIEventSubsidyExpired.DefSQStaticMethod(engine, &AIEventSubsidyExpired::Convert, "Convert", 2, "xx");
+
+ SQAIEventSubsidyExpired.DefSQMethod(engine, &AIEventSubsidyExpired::GetSubsidyID, "GetSubsidyID", 1, "x");
+
+ SQAIEventSubsidyExpired.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AIEventEnginePreview to be used as Squirrel parameter */
+ template <> AIEventEnginePreview *GetParam(ForceType<AIEventEnginePreview *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventEnginePreview *)instance; }
+ template <> AIEventEnginePreview &GetParam(ForceType<AIEventEnginePreview &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventEnginePreview *)instance; }
+ template <> const AIEventEnginePreview *GetParam(ForceType<const AIEventEnginePreview *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventEnginePreview *)instance; }
+ template <> const AIEventEnginePreview &GetParam(ForceType<const AIEventEnginePreview &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventEnginePreview *)instance; }
+ template <> int Return<AIEventEnginePreview *>(HSQUIRRELVM vm, AIEventEnginePreview *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventEnginePreview", res, NULL, DefSQDestructorCallback<AIEventEnginePreview>); return 1; }
+}; // namespace SQConvert
+
+void SQAIEventEnginePreview_Register(Squirrel *engine) {
+ DefSQClass <AIEventEnginePreview> SQAIEventEnginePreview("AIEventEnginePreview");
+ SQAIEventEnginePreview.PreRegister(engine, "AIEvent");
+
+ SQAIEventEnginePreview.DefSQStaticMethod(engine, &AIEventEnginePreview::GetClassName, "GetClassName", 1, "x");
+ SQAIEventEnginePreview.DefSQStaticMethod(engine, &AIEventEnginePreview::Convert, "Convert", 2, "xx");
+
+ SQAIEventEnginePreview.DefSQMethod(engine, &AIEventEnginePreview::GetName, "GetName", 1, "x");
+ SQAIEventEnginePreview.DefSQMethod(engine, &AIEventEnginePreview::GetCargoType, "GetCargoType", 1, "x");
+ SQAIEventEnginePreview.DefSQMethod(engine, &AIEventEnginePreview::GetCapacity, "GetCapacity", 1, "x");
+ SQAIEventEnginePreview.DefSQMethod(engine, &AIEventEnginePreview::GetMaxSpeed, "GetMaxSpeed", 1, "x");
+ SQAIEventEnginePreview.DefSQMethod(engine, &AIEventEnginePreview::GetPrice, "GetPrice", 1, "x");
+ SQAIEventEnginePreview.DefSQMethod(engine, &AIEventEnginePreview::GetRunningCost, "GetRunningCost", 1, "x");
+ SQAIEventEnginePreview.DefSQMethod(engine, &AIEventEnginePreview::GetVehicleType, "GetVehicleType", 1, "x");
+ SQAIEventEnginePreview.DefSQMethod(engine, &AIEventEnginePreview::AcceptPreview, "AcceptPreview", 1, "x");
+
+ SQAIEventEnginePreview.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AIEventCompanyNew to be used as Squirrel parameter */
+ template <> AIEventCompanyNew *GetParam(ForceType<AIEventCompanyNew *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventCompanyNew *)instance; }
+ template <> AIEventCompanyNew &GetParam(ForceType<AIEventCompanyNew &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventCompanyNew *)instance; }
+ template <> const AIEventCompanyNew *GetParam(ForceType<const AIEventCompanyNew *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventCompanyNew *)instance; }
+ template <> const AIEventCompanyNew &GetParam(ForceType<const AIEventCompanyNew &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventCompanyNew *)instance; }
+ template <> int Return<AIEventCompanyNew *>(HSQUIRRELVM vm, AIEventCompanyNew *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventCompanyNew", res, NULL, DefSQDestructorCallback<AIEventCompanyNew>); return 1; }
+}; // namespace SQConvert
+
+void SQAIEventCompanyNew_Register(Squirrel *engine) {
+ DefSQClass <AIEventCompanyNew> SQAIEventCompanyNew("AIEventCompanyNew");
+ SQAIEventCompanyNew.PreRegister(engine, "AIEvent");
+
+ SQAIEventCompanyNew.DefSQStaticMethod(engine, &AIEventCompanyNew::GetClassName, "GetClassName", 1, "x");
+ SQAIEventCompanyNew.DefSQStaticMethod(engine, &AIEventCompanyNew::Convert, "Convert", 2, "xx");
+
+ SQAIEventCompanyNew.DefSQMethod(engine, &AIEventCompanyNew::GetCompanyID, "GetCompanyID", 1, "x");
+
+ SQAIEventCompanyNew.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AIEventCompanyInTrouble to be used as Squirrel parameter */
+ template <> AIEventCompanyInTrouble *GetParam(ForceType<AIEventCompanyInTrouble *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventCompanyInTrouble *)instance; }
+ template <> AIEventCompanyInTrouble &GetParam(ForceType<AIEventCompanyInTrouble &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventCompanyInTrouble *)instance; }
+ template <> const AIEventCompanyInTrouble *GetParam(ForceType<const AIEventCompanyInTrouble *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventCompanyInTrouble *)instance; }
+ template <> const AIEventCompanyInTrouble &GetParam(ForceType<const AIEventCompanyInTrouble &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventCompanyInTrouble *)instance; }
+ template <> int Return<AIEventCompanyInTrouble *>(HSQUIRRELVM vm, AIEventCompanyInTrouble *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventCompanyInTrouble", res, NULL, DefSQDestructorCallback<AIEventCompanyInTrouble>); return 1; }
+}; // namespace SQConvert
+
+void SQAIEventCompanyInTrouble_Register(Squirrel *engine) {
+ DefSQClass <AIEventCompanyInTrouble> SQAIEventCompanyInTrouble("AIEventCompanyInTrouble");
+ SQAIEventCompanyInTrouble.PreRegister(engine, "AIEvent");
+
+ SQAIEventCompanyInTrouble.DefSQStaticMethod(engine, &AIEventCompanyInTrouble::GetClassName, "GetClassName", 1, "x");
+ SQAIEventCompanyInTrouble.DefSQStaticMethod(engine, &AIEventCompanyInTrouble::Convert, "Convert", 2, "xx");
+
+ SQAIEventCompanyInTrouble.DefSQMethod(engine, &AIEventCompanyInTrouble::GetCompanyID, "GetCompanyID", 1, "x");
+
+ SQAIEventCompanyInTrouble.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AIEventCompanyMerger to be used as Squirrel parameter */
+ template <> AIEventCompanyMerger *GetParam(ForceType<AIEventCompanyMerger *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventCompanyMerger *)instance; }
+ template <> AIEventCompanyMerger &GetParam(ForceType<AIEventCompanyMerger &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventCompanyMerger *)instance; }
+ template <> const AIEventCompanyMerger *GetParam(ForceType<const AIEventCompanyMerger *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventCompanyMerger *)instance; }
+ template <> const AIEventCompanyMerger &GetParam(ForceType<const AIEventCompanyMerger &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventCompanyMerger *)instance; }
+ template <> int Return<AIEventCompanyMerger *>(HSQUIRRELVM vm, AIEventCompanyMerger *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventCompanyMerger", res, NULL, DefSQDestructorCallback<AIEventCompanyMerger>); return 1; }
+}; // namespace SQConvert
+
+void SQAIEventCompanyMerger_Register(Squirrel *engine) {
+ DefSQClass <AIEventCompanyMerger> SQAIEventCompanyMerger("AIEventCompanyMerger");
+ SQAIEventCompanyMerger.PreRegister(engine, "AIEvent");
+
+ SQAIEventCompanyMerger.DefSQStaticMethod(engine, &AIEventCompanyMerger::GetClassName, "GetClassName", 1, "x");
+ SQAIEventCompanyMerger.DefSQStaticMethod(engine, &AIEventCompanyMerger::Convert, "Convert", 2, "xx");
+
+ SQAIEventCompanyMerger.DefSQMethod(engine, &AIEventCompanyMerger::GetOldCompanyID, "GetOldCompanyID", 1, "x");
+ SQAIEventCompanyMerger.DefSQMethod(engine, &AIEventCompanyMerger::GetNewCompanyID, "GetNewCompanyID", 1, "x");
+
+ SQAIEventCompanyMerger.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AIEventCompanyBankrupt to be used as Squirrel parameter */
+ template <> AIEventCompanyBankrupt *GetParam(ForceType<AIEventCompanyBankrupt *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventCompanyBankrupt *)instance; }
+ template <> AIEventCompanyBankrupt &GetParam(ForceType<AIEventCompanyBankrupt &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventCompanyBankrupt *)instance; }
+ template <> const AIEventCompanyBankrupt *GetParam(ForceType<const AIEventCompanyBankrupt *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventCompanyBankrupt *)instance; }
+ template <> const AIEventCompanyBankrupt &GetParam(ForceType<const AIEventCompanyBankrupt &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventCompanyBankrupt *)instance; }
+ template <> int Return<AIEventCompanyBankrupt *>(HSQUIRRELVM vm, AIEventCompanyBankrupt *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventCompanyBankrupt", res, NULL, DefSQDestructorCallback<AIEventCompanyBankrupt>); return 1; }
+}; // namespace SQConvert
+
+void SQAIEventCompanyBankrupt_Register(Squirrel *engine) {
+ DefSQClass <AIEventCompanyBankrupt> SQAIEventCompanyBankrupt("AIEventCompanyBankrupt");
+ SQAIEventCompanyBankrupt.PreRegister(engine, "AIEvent");
+
+ SQAIEventCompanyBankrupt.DefSQStaticMethod(engine, &AIEventCompanyBankrupt::GetClassName, "GetClassName", 1, "x");
+ SQAIEventCompanyBankrupt.DefSQStaticMethod(engine, &AIEventCompanyBankrupt::Convert, "Convert", 2, "xx");
+
+ SQAIEventCompanyBankrupt.DefSQMethod(engine, &AIEventCompanyBankrupt::GetCompanyID, "GetCompanyID", 1, "x");
+
+ SQAIEventCompanyBankrupt.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AIEventVehicleLost to be used as Squirrel parameter */
+ template <> AIEventVehicleLost *GetParam(ForceType<AIEventVehicleLost *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventVehicleLost *)instance; }
+ template <> AIEventVehicleLost &GetParam(ForceType<AIEventVehicleLost &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventVehicleLost *)instance; }
+ template <> const AIEventVehicleLost *GetParam(ForceType<const AIEventVehicleLost *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventVehicleLost *)instance; }
+ template <> const AIEventVehicleLost &GetParam(ForceType<const AIEventVehicleLost &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventVehicleLost *)instance; }
+ template <> int Return<AIEventVehicleLost *>(HSQUIRRELVM vm, AIEventVehicleLost *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventVehicleLost", res, NULL, DefSQDestructorCallback<AIEventVehicleLost>); return 1; }
+}; // namespace SQConvert
+
+void SQAIEventVehicleLost_Register(Squirrel *engine) {
+ DefSQClass <AIEventVehicleLost> SQAIEventVehicleLost("AIEventVehicleLost");
+ SQAIEventVehicleLost.PreRegister(engine, "AIEvent");
+
+ SQAIEventVehicleLost.DefSQStaticMethod(engine, &AIEventVehicleLost::GetClassName, "GetClassName", 1, "x");
+ SQAIEventVehicleLost.DefSQStaticMethod(engine, &AIEventVehicleLost::Convert, "Convert", 2, "xx");
+
+ SQAIEventVehicleLost.DefSQMethod(engine, &AIEventVehicleLost::GetVehicleID, "GetVehicleID", 1, "x");
+
+ SQAIEventVehicleLost.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AIEventVehicleWaitingInDepot to be used as Squirrel parameter */
+ template <> AIEventVehicleWaitingInDepot *GetParam(ForceType<AIEventVehicleWaitingInDepot *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventVehicleWaitingInDepot *)instance; }
+ template <> AIEventVehicleWaitingInDepot &GetParam(ForceType<AIEventVehicleWaitingInDepot &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventVehicleWaitingInDepot *)instance; }
+ template <> const AIEventVehicleWaitingInDepot *GetParam(ForceType<const AIEventVehicleWaitingInDepot *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventVehicleWaitingInDepot *)instance; }
+ template <> const AIEventVehicleWaitingInDepot &GetParam(ForceType<const AIEventVehicleWaitingInDepot &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventVehicleWaitingInDepot *)instance; }
+ template <> int Return<AIEventVehicleWaitingInDepot *>(HSQUIRRELVM vm, AIEventVehicleWaitingInDepot *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventVehicleWaitingInDepot", res, NULL, DefSQDestructorCallback<AIEventVehicleWaitingInDepot>); return 1; }
+}; // namespace SQConvert
+
+void SQAIEventVehicleWaitingInDepot_Register(Squirrel *engine) {
+ DefSQClass <AIEventVehicleWaitingInDepot> SQAIEventVehicleWaitingInDepot("AIEventVehicleWaitingInDepot");
+ SQAIEventVehicleWaitingInDepot.PreRegister(engine, "AIEvent");
+
+ SQAIEventVehicleWaitingInDepot.DefSQStaticMethod(engine, &AIEventVehicleWaitingInDepot::GetClassName, "GetClassName", 1, "x");
+ SQAIEventVehicleWaitingInDepot.DefSQStaticMethod(engine, &AIEventVehicleWaitingInDepot::Convert, "Convert", 2, "xx");
+
+ SQAIEventVehicleWaitingInDepot.DefSQMethod(engine, &AIEventVehicleWaitingInDepot::GetVehicleID, "GetVehicleID", 1, "x");
+
+ SQAIEventVehicleWaitingInDepot.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AIEventVehicleUnprofitable to be used as Squirrel parameter */
+ template <> AIEventVehicleUnprofitable *GetParam(ForceType<AIEventVehicleUnprofitable *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventVehicleUnprofitable *)instance; }
+ template <> AIEventVehicleUnprofitable &GetParam(ForceType<AIEventVehicleUnprofitable &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventVehicleUnprofitable *)instance; }
+ template <> const AIEventVehicleUnprofitable *GetParam(ForceType<const AIEventVehicleUnprofitable *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventVehicleUnprofitable *)instance; }
+ template <> const AIEventVehicleUnprofitable &GetParam(ForceType<const AIEventVehicleUnprofitable &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventVehicleUnprofitable *)instance; }
+ template <> int Return<AIEventVehicleUnprofitable *>(HSQUIRRELVM vm, AIEventVehicleUnprofitable *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventVehicleUnprofitable", res, NULL, DefSQDestructorCallback<AIEventVehicleUnprofitable>); return 1; }
+}; // namespace SQConvert
+
+void SQAIEventVehicleUnprofitable_Register(Squirrel *engine) {
+ DefSQClass <AIEventVehicleUnprofitable> SQAIEventVehicleUnprofitable("AIEventVehicleUnprofitable");
+ SQAIEventVehicleUnprofitable.PreRegister(engine, "AIEvent");
+
+ SQAIEventVehicleUnprofitable.DefSQStaticMethod(engine, &AIEventVehicleUnprofitable::GetClassName, "GetClassName", 1, "x");
+ SQAIEventVehicleUnprofitable.DefSQStaticMethod(engine, &AIEventVehicleUnprofitable::Convert, "Convert", 2, "xx");
+
+ SQAIEventVehicleUnprofitable.DefSQMethod(engine, &AIEventVehicleUnprofitable::GetVehicleID, "GetVehicleID", 1, "x");
+
+ SQAIEventVehicleUnprofitable.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AIEventIndustryOpen to be used as Squirrel parameter */
+ template <> AIEventIndustryOpen *GetParam(ForceType<AIEventIndustryOpen *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventIndustryOpen *)instance; }
+ template <> AIEventIndustryOpen &GetParam(ForceType<AIEventIndustryOpen &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventIndustryOpen *)instance; }
+ template <> const AIEventIndustryOpen *GetParam(ForceType<const AIEventIndustryOpen *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventIndustryOpen *)instance; }
+ template <> const AIEventIndustryOpen &GetParam(ForceType<const AIEventIndustryOpen &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventIndustryOpen *)instance; }
+ template <> int Return<AIEventIndustryOpen *>(HSQUIRRELVM vm, AIEventIndustryOpen *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventIndustryOpen", res, NULL, DefSQDestructorCallback<AIEventIndustryOpen>); return 1; }
+}; // namespace SQConvert
+
+void SQAIEventIndustryOpen_Register(Squirrel *engine) {
+ DefSQClass <AIEventIndustryOpen> SQAIEventIndustryOpen("AIEventIndustryOpen");
+ SQAIEventIndustryOpen.PreRegister(engine, "AIEvent");
+
+ SQAIEventIndustryOpen.DefSQStaticMethod(engine, &AIEventIndustryOpen::GetClassName, "GetClassName", 1, "x");
+ SQAIEventIndustryOpen.DefSQStaticMethod(engine, &AIEventIndustryOpen::Convert, "Convert", 2, "xx");
+
+ SQAIEventIndustryOpen.DefSQMethod(engine, &AIEventIndustryOpen::GetIndustryID, "GetIndustryID", 1, "x");
+
+ SQAIEventIndustryOpen.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AIEventIndustryClose to be used as Squirrel parameter */
+ template <> AIEventIndustryClose *GetParam(ForceType<AIEventIndustryClose *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventIndustryClose *)instance; }
+ template <> AIEventIndustryClose &GetParam(ForceType<AIEventIndustryClose &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventIndustryClose *)instance; }
+ template <> const AIEventIndustryClose *GetParam(ForceType<const AIEventIndustryClose *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventIndustryClose *)instance; }
+ template <> const AIEventIndustryClose &GetParam(ForceType<const AIEventIndustryClose &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventIndustryClose *)instance; }
+ template <> int Return<AIEventIndustryClose *>(HSQUIRRELVM vm, AIEventIndustryClose *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventIndustryClose", res, NULL, DefSQDestructorCallback<AIEventIndustryClose>); return 1; }
+}; // namespace SQConvert
+
+void SQAIEventIndustryClose_Register(Squirrel *engine) {
+ DefSQClass <AIEventIndustryClose> SQAIEventIndustryClose("AIEventIndustryClose");
+ SQAIEventIndustryClose.PreRegister(engine, "AIEvent");
+
+ SQAIEventIndustryClose.DefSQStaticMethod(engine, &AIEventIndustryClose::GetClassName, "GetClassName", 1, "x");
+ SQAIEventIndustryClose.DefSQStaticMethod(engine, &AIEventIndustryClose::Convert, "Convert", 2, "xx");
+
+ SQAIEventIndustryClose.DefSQMethod(engine, &AIEventIndustryClose::GetIndustryID, "GetIndustryID", 1, "x");
+
+ SQAIEventIndustryClose.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AIEventEngineAvailable to be used as Squirrel parameter */
+ template <> AIEventEngineAvailable *GetParam(ForceType<AIEventEngineAvailable *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventEngineAvailable *)instance; }
+ template <> AIEventEngineAvailable &GetParam(ForceType<AIEventEngineAvailable &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventEngineAvailable *)instance; }
+ template <> const AIEventEngineAvailable *GetParam(ForceType<const AIEventEngineAvailable *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventEngineAvailable *)instance; }
+ template <> const AIEventEngineAvailable &GetParam(ForceType<const AIEventEngineAvailable &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventEngineAvailable *)instance; }
+ template <> int Return<AIEventEngineAvailable *>(HSQUIRRELVM vm, AIEventEngineAvailable *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventEngineAvailable", res, NULL, DefSQDestructorCallback<AIEventEngineAvailable>); return 1; }
+}; // namespace SQConvert
+
+void SQAIEventEngineAvailable_Register(Squirrel *engine) {
+ DefSQClass <AIEventEngineAvailable> SQAIEventEngineAvailable("AIEventEngineAvailable");
+ SQAIEventEngineAvailable.PreRegister(engine, "AIEvent");
+
+ SQAIEventEngineAvailable.DefSQStaticMethod(engine, &AIEventEngineAvailable::GetClassName, "GetClassName", 1, "x");
+ SQAIEventEngineAvailable.DefSQStaticMethod(engine, &AIEventEngineAvailable::Convert, "Convert", 2, "xx");
+
+ SQAIEventEngineAvailable.DefSQMethod(engine, &AIEventEngineAvailable::GetEngineID, "GetEngineID", 1, "x");
+
+ SQAIEventEngineAvailable.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AIEventStationFirstVehicle to be used as Squirrel parameter */
+ template <> AIEventStationFirstVehicle *GetParam(ForceType<AIEventStationFirstVehicle *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventStationFirstVehicle *)instance; }
+ template <> AIEventStationFirstVehicle &GetParam(ForceType<AIEventStationFirstVehicle &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventStationFirstVehicle *)instance; }
+ template <> const AIEventStationFirstVehicle *GetParam(ForceType<const AIEventStationFirstVehicle *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIEventStationFirstVehicle *)instance; }
+ template <> const AIEventStationFirstVehicle &GetParam(ForceType<const AIEventStationFirstVehicle &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIEventStationFirstVehicle *)instance; }
+ template <> int Return<AIEventStationFirstVehicle *>(HSQUIRRELVM vm, AIEventStationFirstVehicle *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIEventStationFirstVehicle", res, NULL, DefSQDestructorCallback<AIEventStationFirstVehicle>); return 1; }
+}; // namespace SQConvert
+
+void SQAIEventStationFirstVehicle_Register(Squirrel *engine) {
+ DefSQClass <AIEventStationFirstVehicle> SQAIEventStationFirstVehicle("AIEventStationFirstVehicle");
+ SQAIEventStationFirstVehicle.PreRegister(engine, "AIEvent");
+
+ SQAIEventStationFirstVehicle.DefSQStaticMethod(engine, &AIEventStationFirstVehicle::GetClassName, "GetClassName", 1, "x");
+ SQAIEventStationFirstVehicle.DefSQStaticMethod(engine, &AIEventStationFirstVehicle::Convert, "Convert", 2, "xx");
+
+ SQAIEventStationFirstVehicle.DefSQMethod(engine, &AIEventStationFirstVehicle::GetStationID, "GetStationID", 1, "x");
+ SQAIEventStationFirstVehicle.DefSQMethod(engine, &AIEventStationFirstVehicle::GetVehicleID, "GetVehicleID", 1, "x");
+
+ SQAIEventStationFirstVehicle.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_execmode.cpp b/src/ai/api/ai_execmode.cpp
new file mode 100644
index 000000000..941718bee
--- /dev/null
+++ b/src/ai/api/ai_execmode.cpp
@@ -0,0 +1,26 @@
+/* $Id$ */
+
+/** @file ai_execmode.cpp Implementation of AIExecMode. */
+
+#include "ai_execmode.hpp"
+#include "../../command_type.h"
+
+bool AIExecMode::ModeProc(TileIndex tile, uint32 p1, uint32 p2, uint procc, CommandCost costs)
+{
+ /* In execution mode we only return 'true', telling the DoCommand it
+ * should continue with the real execution of the command. */
+ return true;
+}
+
+AIExecMode::AIExecMode()
+{
+ this->last_mode = this->GetDoCommandMode();
+ this->last_instance = this->GetDoCommandModeInstance();
+ this->SetDoCommandMode(&AIExecMode::ModeProc, this);
+}
+
+AIExecMode::~AIExecMode()
+{
+ assert(this->GetDoCommandModeInstance() == this);
+ this->SetDoCommandMode(this->last_mode, this->last_instance);
+}
diff --git a/src/ai/api/ai_execmode.hpp b/src/ai/api/ai_execmode.hpp
new file mode 100644
index 000000000..4f6a47bc4
--- /dev/null
+++ b/src/ai/api/ai_execmode.hpp
@@ -0,0 +1,46 @@
+/* $Id$ */
+
+/** @file ai_execmode.hpp Switch the AI to Execute Mode. */
+
+#ifndef AI_EXECMODE_HPP
+#define AI_EXECMODE_HPP
+
+#include "ai_object.hpp"
+
+/**
+ * Class to switch current mode to Execute Mode.
+ * If you create an instance of this class, the mode will be switched to
+ * Execute. The original mode is stored and recovered from when ever the
+ * instance is destroyed.
+ * In Execute mode all commands you do are executed for real.
+ */
+class AIExecMode : public AIObject {
+public:
+ static const char *GetClassName() { return "AIExecMode"; }
+
+private:
+ AIModeProc *last_mode;
+ AIObject *last_instance;
+
+protected:
+ /**
+ * The callback proc for Execute mode.
+ */
+ static bool ModeProc(TileIndex tile, uint32 p1, uint32 p2, uint procc, CommandCost costs);
+
+public:
+ /**
+ * Creating instance of this class switches the build mode to Execute.
+ * @note When the instance is destroyed, he restores the mode that was
+ * current when the instance was created!
+ */
+ AIExecMode();
+
+ /**
+ * Destroying this instance reset the building mode to the mode it was
+ * in when the instance was created.
+ */
+ ~AIExecMode();
+};
+
+#endif /* AI_EXECMODE_HPP */
diff --git a/src/ai/api/ai_execmode.hpp.sq b/src/ai/api/ai_execmode.hpp.sq
new file mode 100644
index 000000000..de78e9b6b
--- /dev/null
+++ b/src/ai/api/ai_execmode.hpp.sq
@@ -0,0 +1,23 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_execmode.hpp"
+
+namespace SQConvert {
+ /* Allow AIExecMode to be used as Squirrel parameter */
+ template <> AIExecMode *GetParam(ForceType<AIExecMode *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIExecMode *)instance; }
+ template <> AIExecMode &GetParam(ForceType<AIExecMode &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIExecMode *)instance; }
+ template <> const AIExecMode *GetParam(ForceType<const AIExecMode *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIExecMode *)instance; }
+ template <> const AIExecMode &GetParam(ForceType<const AIExecMode &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIExecMode *)instance; }
+ template <> int Return<AIExecMode *>(HSQUIRRELVM vm, AIExecMode *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIExecMode", res, NULL, DefSQDestructorCallback<AIExecMode>); return 1; }
+}; // namespace SQConvert
+
+void SQAIExecMode_Register(Squirrel *engine) {
+ DefSQClass <AIExecMode> SQAIExecMode("AIExecMode");
+ SQAIExecMode.PreRegister(engine);
+ SQAIExecMode.AddConstructor<void (AIExecMode::*)(), 1>(engine, "x");
+
+ SQAIExecMode.DefSQStaticMethod(engine, &AIExecMode::GetClassName, "GetClassName", 1, "x");
+
+ SQAIExecMode.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_gamesettings.cpp b/src/ai/api/ai_gamesettings.cpp
new file mode 100644
index 000000000..81b187d73
--- /dev/null
+++ b/src/ai/api/ai_gamesettings.cpp
@@ -0,0 +1,38 @@
+/* $Id$ */
+
+/** @file ai_gamesettings.cpp Implementation of AIGameSettings. */
+
+#include "ai_gamesettings.hpp"
+#include "../../settings_internal.h"
+#include "../../saveload/saveload.h"
+
+/* static */ bool AIGameSettings::IsValid(const char *setting)
+{
+ uint i;
+ const SettingDesc *sd = GetPatchFromName(setting, &i);
+ return sd != NULL && sd->desc.cmd != SDT_STRING;
+}
+
+/* static */ int32 AIGameSettings::GetValue(const char *setting)
+{
+ if (!IsValid(setting)) return -1;
+
+ uint i;
+ const SettingDesc *sd = GetPatchFromName(setting, &i);
+
+ void *ptr = GetVariableAddress(&_settings_game, &sd->save);
+ if (sd->desc.cmd == SDT_BOOLX) return *(bool*)ptr;
+
+ return (int32)ReadValue(ptr, sd->save.conv);
+}
+
+/* static */ bool AIGameSettings::IsDisabledVehicleType(AIVehicle::VehicleType vehicle_type)
+{
+ switch (vehicle_type) {
+ case AIVehicle::VEHICLE_RAIL: return _settings_game.ai.ai_disable_veh_train;
+ case AIVehicle::VEHICLE_ROAD: return _settings_game.ai.ai_disable_veh_roadveh;
+ case AIVehicle::VEHICLE_WATER: return _settings_game.ai.ai_disable_veh_ship;
+ case AIVehicle::VEHICLE_AIR: return _settings_game.ai.ai_disable_veh_aircraft;
+ default: return true;
+ }
+}
diff --git a/src/ai/api/ai_gamesettings.hpp b/src/ai/api/ai_gamesettings.hpp
new file mode 100644
index 000000000..67c8d964e
--- /dev/null
+++ b/src/ai/api/ai_gamesettings.hpp
@@ -0,0 +1,67 @@
+/* $Id$ */
+
+/** @file ai_gamesettings.hpp Everything to read game settings. */
+
+#ifndef AI_GAMESETTINGS_HPP
+#define AI_GAMESETTINGS_HPP
+
+#include "ai_object.hpp"
+#include "ai_vehicle.hpp"
+
+/**
+ * Class that handles all game settings related functions.
+ *
+ * @note AIGameSettings::IsValid and AIGameSettings::GetValue are functions
+ * that rely on the settings as OpenTTD stores them in savegame and
+ * openttd.cfg. No guarantees can be given on the long term validity,
+ * consistency and stability of the names, values and value ranges.
+ * Using these settings can be dangerous and could cause issues in
+ * future versions. To make sure that a setting still exists in the
+ * current version you have to run AIGameSettings::IsValid before
+ * accessing it.
+ *
+ * @note The names of the setting for AIGameSettings::IsValid and
+ * AIGameSettings::GetValue are the same ones as those that are shown by
+ * the list_patches command in the in-game console. Settings that are
+ * string based are NOT supported and AIGAmeSettings::IsValid will return
+ * false for them. These settings will not be supported either because
+ * they have no relevance for the AI (default client names, server IPs,
+ * etc.).
+ */
+class AIGameSettings : public AIObject {
+public:
+ static const char *GetClassName() { return "AIGameSettings"; }
+
+ /**
+ * Is the given game setting a valid setting for this instance of OpenTTD?
+ * @param setting The setting to check for existence.
+ * @warning Results of this function are not governed by the API. This means
+ * that a setting that previously existed can be gone or has
+ * changed it's name.
+ * @note Results achieved in the past offer no gurantee for the future.
+ * @return True if and only if the setting is valid.
+ */
+ static bool IsValid(const char *setting);
+
+ /**
+ * Gets the value of the game setting.
+ * @param setting The setting to get the value of.
+ * @pre IsValid(setting).
+ * @warning Results of this function are not governed by the API. This means
+ * that the value of settings may be out of the expected range. It
+ * also means that a setting that previously existed can be gone or
+ * has changed it's name/characteristics.
+ * @note Results achieved in the past offer no gurantee for the future.
+ * @return The value for the setting.
+ */
+ static int32 GetValue(const char *setting);
+
+ /**
+ * Checks whether the given vehicle-type is disabled for AIs.
+ * @param vehicle_type The vehicle-type to check.
+ * @return True if the vehicle-type is disabled.
+ */
+ static bool IsDisabledVehicleType(AIVehicle::VehicleType vehicle_type);
+};
+
+#endif /* AI_GAMESETTINGS_HPP */
diff --git a/src/ai/api/ai_gamesettings.hpp.sq b/src/ai/api/ai_gamesettings.hpp.sq
new file mode 100644
index 000000000..e21812c08
--- /dev/null
+++ b/src/ai/api/ai_gamesettings.hpp.sq
@@ -0,0 +1,26 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_gamesettings.hpp"
+
+namespace SQConvert {
+ /* Allow AIGameSettings to be used as Squirrel parameter */
+ template <> AIGameSettings *GetParam(ForceType<AIGameSettings *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIGameSettings *)instance; }
+ template <> AIGameSettings &GetParam(ForceType<AIGameSettings &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIGameSettings *)instance; }
+ template <> const AIGameSettings *GetParam(ForceType<const AIGameSettings *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIGameSettings *)instance; }
+ template <> const AIGameSettings &GetParam(ForceType<const AIGameSettings &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIGameSettings *)instance; }
+ template <> int Return<AIGameSettings *>(HSQUIRRELVM vm, AIGameSettings *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIGameSettings", res, NULL, DefSQDestructorCallback<AIGameSettings>); return 1; }
+}; // namespace SQConvert
+
+void SQAIGameSettings_Register(Squirrel *engine) {
+ DefSQClass <AIGameSettings> SQAIGameSettings("AIGameSettings");
+ SQAIGameSettings.PreRegister(engine);
+ SQAIGameSettings.AddConstructor<void (AIGameSettings::*)(), 1>(engine, "x");
+
+ SQAIGameSettings.DefSQStaticMethod(engine, &AIGameSettings::GetClassName, "GetClassName", 1, "x");
+ SQAIGameSettings.DefSQStaticMethod(engine, &AIGameSettings::IsValid, "IsValid", 2, "xs");
+ SQAIGameSettings.DefSQStaticMethod(engine, &AIGameSettings::GetValue, "GetValue", 2, "xs");
+ SQAIGameSettings.DefSQStaticMethod(engine, &AIGameSettings::IsDisabledVehicleType, "IsDisabledVehicleType", 2, "xi");
+
+ SQAIGameSettings.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_group.cpp b/src/ai/api/ai_group.cpp
new file mode 100644
index 000000000..a6365450b
--- /dev/null
+++ b/src/ai/api/ai_group.cpp
@@ -0,0 +1,128 @@
+/* $Id$ */
+
+/** @file ai_group.cpp Implementation of AIGroup. */
+
+#include "ai_group.hpp"
+#include "ai_vehicle.hpp"
+#include "ai_engine.hpp"
+#include "../ai_instance.hpp"
+#include "../../openttd.h"
+#include "../../company_func.h"
+#include "../../group.h"
+#include "../../string_func.h"
+#include "../../strings_func.h"
+#include "../../core/alloc_func.hpp"
+#include "../../command_func.h"
+#include "../../autoreplace_func.h"
+#include "table/strings.h"
+
+/* static */ bool AIGroup::IsValidGroup(GroupID group_id)
+{
+ return ::IsValidGroupID(group_id) && ::GetGroup(group_id)->owner == _current_company;
+}
+
+/* static */ AIGroup::GroupID AIGroup::CreateGroup(AIVehicle::VehicleType vehicle_type)
+{
+ if (!AIObject::DoCommand(0, (::VehicleType)vehicle_type, 0, CMD_CREATE_GROUP, NULL, &AIInstance::DoCommandReturnGroupID)) return INVALID_GROUP;
+
+ /* In case of test-mode, we return GroupID 0 */
+ return (AIGroup::GroupID)0;
+}
+
+/* static */ bool AIGroup::DeleteGroup(GroupID group_id)
+{
+ EnforcePrecondition(false, IsValidGroup(group_id));
+
+ return AIObject::DoCommand(0, group_id, 0, CMD_DELETE_GROUP);
+}
+
+/* static */ AIVehicle::VehicleType AIGroup::GetVehicleType(GroupID group_id)
+{
+ if (!IsValidGroup(group_id)) return AIVehicle::VEHICLE_INVALID;
+
+ return (AIVehicle::VehicleType)((::VehicleType)::GetGroup(group_id)->vehicle_type);
+}
+
+/* static */ bool AIGroup::SetName(GroupID group_id, const char *name)
+{
+ EnforcePrecondition(false, IsValidGroup(group_id));
+ EnforcePrecondition(false, !::StrEmpty(name));
+ EnforcePreconditionCustomError(false, ::strlen(name) < MAX_LENGTH_GROUP_NAME_BYTES, AIError::ERR_PRECONDITION_STRING_TOO_LONG);
+
+ return AIObject::DoCommand(0, group_id, 0, CMD_RENAME_GROUP, name);
+}
+
+/* static */ const char *AIGroup::GetName(GroupID group_id)
+{
+ if (!IsValidGroup(group_id)) return NULL;
+
+ static const int len = 64;
+ char *group_name = MallocT<char>(len);
+
+ ::SetDParam(0, group_id);
+ ::GetString(group_name, STR_GROUP_NAME, &group_name[len - 1]);
+ return group_name;
+}
+
+/* static */ bool AIGroup::EnableAutoReplaceProtection(GroupID group_id, bool enable)
+{
+ EnforcePrecondition(false, IsValidGroup(group_id));
+
+ return AIObject::DoCommand(0, group_id, enable ? 1 : 0, CMD_SET_GROUP_REPLACE_PROTECTION);
+}
+
+/* static */ bool AIGroup::GetAutoReplaceProtection(GroupID group_id)
+{
+ if (!IsValidGroup(group_id)) return false;
+
+ return ::GetGroup(group_id)->replace_protection;
+}
+
+/* static */ int32 AIGroup::GetNumEngines(GroupID group_id, EngineID engine_id)
+{
+ if (!IsValidGroup(group_id) && group_id != DEFAULT_GROUP && group_id != ALL_GROUP) return -1;
+
+ return GetGroupNumEngines(_current_company, group_id, engine_id);
+}
+
+/* static */ bool AIGroup::MoveVehicle(GroupID group_id, VehicleID vehicle_id)
+{
+ EnforcePrecondition(false, IsValidGroup(group_id) || group_id == DEFAULT_GROUP);
+ EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
+
+ return AIObject::DoCommand(0, group_id, vehicle_id, CMD_ADD_VEHICLE_GROUP);
+}
+
+/* static */ bool AIGroup::EnableWagonRemoval(bool enable_removal)
+{
+ if (HasWagonRemoval() == enable_removal) return true;
+
+ return AIObject::DoCommand(0, 5, enable_removal ? 1 : 0, CMD_SET_AUTOREPLACE);
+}
+
+/* static */ bool AIGroup::HasWagonRemoval()
+{
+ return ::GetCompany(_current_company)->renew_keep_length;
+}
+
+/* static */ bool AIGroup::SetAutoReplace(GroupID group_id, EngineID engine_id_old, EngineID engine_id_new)
+{
+ EnforcePrecondition(false, IsValidGroup(group_id) || group_id == ALL_GROUP);
+ EnforcePrecondition(false, AIEngine::IsValidEngine(engine_id_new));
+
+ return AIObject::DoCommand(0, 3 | (group_id << 16), (engine_id_new << 16) | engine_id_old, CMD_SET_AUTOREPLACE);
+}
+
+/* static */ EngineID AIGroup::GetEngineReplacement(GroupID group_id, EngineID engine_id)
+{
+ if (!IsValidGroup(group_id) && group_id != ALL_GROUP) return ::INVALID_ENGINE;
+
+ return ::EngineReplacementForCompany(GetCompany(_current_company), engine_id, group_id);
+}
+
+/* static */ bool AIGroup::StopAutoReplace(GroupID group_id, EngineID engine_id)
+{
+ EnforcePrecondition(false, IsValidGroup(group_id) || group_id == ALL_GROUP);
+
+ return AIObject::DoCommand(0, 3 | (group_id << 16), (::INVALID_ENGINE << 16) | engine_id, CMD_SET_AUTOREPLACE);
+}
diff --git a/src/ai/api/ai_group.hpp b/src/ai/api/ai_group.hpp
new file mode 100644
index 000000000..84569c562
--- /dev/null
+++ b/src/ai/api/ai_group.hpp
@@ -0,0 +1,170 @@
+/* $Id$ */
+
+/** @file ai_group.hpp Everything to put vehicles into groups. */
+
+#ifndef AI_GROUP_HPP
+#define AI_GROUP_HPP
+
+#include "ai_object.hpp"
+#include "ai_vehicle.hpp"
+
+/**
+ * Class that handles all group related functions.
+ */
+class AIGroup : public AIObject {
+public:
+ static const char *GetClassName() { return "AIGroup"; }
+
+ /**
+ * The group IDs of some special groups.
+ */
+ enum GroupID {
+ /* Values are important, as they represent the internal state of the game (see group_type.h). */
+ ALL_GROUP = 0xFFFD, //!< All vehicles are in this group.
+ DEFAULT_GROUP = 0xFFFE, //!< Vehicles not put in any other group are in this one.
+ INVALID_GROUP = 0xFFFF, //!< An invalid group id.
+ };
+
+ /**
+ * Checks whether the given group is valid.
+ * @param group_id The group to check.
+ * @pre group_id != DEFAULT_GROUP && group_id != ALL_GROUP.
+ * @return True if and only if the group is valid.
+ */
+ static bool IsValidGroup(GroupID group_id);
+
+ /**
+ * Create a new group.
+ * @param vehicle_type The type of vehicle to create a group for.
+ * @return The GroupID of the new group, or an invalid GroupID when
+ * it failed. Check the return value using IsValidGroup(). In test-mode
+ * 0 is returned if it was successful; any other value indicates failure.
+ */
+ static GroupID CreateGroup(AIVehicle::VehicleType vehicle_type);
+
+ /**
+ * Delete the given group. When the deletion succeeds all vehicles in the
+ * given group will move to the DEFAULT_GROUP.
+ * @param group_id The group to delete.
+ * @pre IsValidGroup(group_id).
+ * @return True if and only if the group was succesfully deleted.
+ */
+ static bool DeleteGroup(GroupID group_id);
+
+ /**
+ * Get the vehicle type of a group.
+ * @param group_id The group to get the type from.
+ * @pre IsValidGroup(group_id).
+ * @return The vehicletype of the given group.
+ */
+ static AIVehicle::VehicleType GetVehicleType(GroupID group_id);
+
+ /**
+ * Set the name of a group.
+ * @param group_id The group to set the name for.
+ * @param name The name for the group.
+ * @pre IsValidGroup(group_id).
+ * @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 and only if the name was changed.
+ */
+ static bool SetName(GroupID group_id, const char *name);
+
+ /**
+ * Get the name of a group.
+ * @param group_id The group to get the name of.
+ * @pre IsValidGroup(group_id).
+ * @return The name the group has.
+ */
+ static const char *GetName(GroupID group_id);
+
+ /**
+ * Enable or disable autoreplace protected. If the protection is
+ * enabled, global autoreplace won't affect vehicles in this group.
+ * @param group_id The group to change the protection for.
+ * @param enable True if protection should be enabled.
+ * @pre IsValidGroup(group_id).
+ * @return True if and only if the protection was succesfully changed.
+ */
+ static bool EnableAutoReplaceProtection(GroupID group_id, bool enable);
+
+ /**
+ * Get the autoreplace protection status.
+ * @param group_id The group to get the protection status for.
+ * @pre IsValidGroup(group_id).
+ * @return The autoreplace protection status for the given group.
+ */
+ static bool GetAutoReplaceProtection(GroupID group_id);
+
+ /**
+ * Get the number of engines in a given group.
+ * @param group_id The group to get the number of engines in.
+ * @param engine_id The engine id to count.
+ * @pre IsValidGroup(group_id) || group_id == ALL_GROUP || group_id == DEFAULT_GROUP.
+ * @return The number of engines with id engine_id in the group with id group_id.
+ */
+ static int32 GetNumEngines(GroupID group_id, EngineID engine_id);
+
+ /**
+ * Move a vehicle to a group.
+ * @param group_id The group to move the vehicel to.
+ * @param vehicle_id The vehicle to move to the group.
+ * @pre IsValidGroup(group_id) || group_id == DEFAULT_GROUP.
+ * @pre AIVehicle::IsValidVehicle(vehicle_id).
+ * @return True if and only if the vehicle was succesfully moved to the group.
+ * @note A vehicle can be in only one group at the same time. To remove it from
+ * a group, move it to another or to DEFAULT_GROUP. Moving the vehicle to the
+ * given group means removing it from another group.
+ */
+ static bool MoveVehicle(GroupID group_id, VehicleID vehicle_id);
+
+ /**
+ * Enable or disable the removal of wagons when a (part of a) vehicle is
+ * (auto)replaced with a longer variant (longer wagons or longer engines)
+ * If enabled, wagons are removed from the end of the vehicle until it
+ * fits in the same number of tiles as it did before.
+ * @param keep_length If true, wagons will be removed if the a new engine is longer.
+ * @return True if and only if the value was succesfully changed.
+ */
+ static bool EnableWagonRemoval(bool keep_length);
+
+ /**
+ * Get the current status of wagon removal.
+ * @return Whether or not wagon removal is enabled.
+ */
+ static bool HasWagonRemoval();
+
+ /**
+ * Start replacing all vehicles with a specified engine with another engine.
+ * @param group_id The group to replace vehicles from. Use ALL_GROUP to replace
+ * vehicles from all groups that haven't set autoreplace protection.
+ * @param engine_id_old The engine id to start replacing.
+ * @param engine_id_new The engine id to replace with.
+ * @pre IsValidGroup(group_id) || group_id == ALL_GROUP.
+ * @pre AIEngine.IsValidEngine(engine_id_new).
+ * @note To stop autoreplacing engine_id_old, call StopAutoReplace(group_id, engine_id_old).
+ */
+ static bool SetAutoReplace(GroupID group_id, EngineID engine_id_old, EngineID engine_id_new);
+
+ /**
+ * Get the EngineID the given EngineID is replaced with.
+ * @param group_id The group to get the replacement from.
+ * @param engine_id The engine that is being replaced.
+ * @pre IsValidGroup(group_id) || group_id == ALL_GROUP.
+ * @return The EngineID that is replacing engine_id or an invalid EngineID
+ * in case engine_id is not begin replaced.
+ */
+ static EngineID GetEngineReplacement(GroupID group_id, EngineID engine_id);
+
+ /**
+ * Stop replacing a certain engine in the specified group.
+ * @param group_id The group to stop replacing the engine in.
+ * @param engine_id The engine id to stop replacing with another engine.
+ * @pre IsValidGroup(group_id) || group_id == ALL_GROUP.
+ * @return True if and if the replacing was succesfully stopped.
+ */
+ static bool StopAutoReplace(GroupID group_id, EngineID engine_id);
+};
+
+#endif /* AI_GROUP_HPP */
diff --git a/src/ai/api/ai_group.hpp.sq b/src/ai/api/ai_group.hpp.sq
new file mode 100644
index 000000000..3556f5cc9
--- /dev/null
+++ b/src/ai/api/ai_group.hpp.sq
@@ -0,0 +1,46 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_group.hpp"
+
+namespace SQConvert {
+ /* Allow enums to be used as Squirrel parameters */
+ template <> AIGroup::GroupID GetParam(ForceType<AIGroup::GroupID>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIGroup::GroupID)tmp; }
+ template <> int Return<AIGroup::GroupID>(HSQUIRRELVM vm, AIGroup::GroupID res) { sq_pushinteger(vm, (int32)res); return 1; }
+
+ /* Allow AIGroup to be used as Squirrel parameter */
+ template <> AIGroup *GetParam(ForceType<AIGroup *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIGroup *)instance; }
+ template <> AIGroup &GetParam(ForceType<AIGroup &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIGroup *)instance; }
+ template <> const AIGroup *GetParam(ForceType<const AIGroup *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIGroup *)instance; }
+ template <> const AIGroup &GetParam(ForceType<const AIGroup &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIGroup *)instance; }
+ template <> int Return<AIGroup *>(HSQUIRRELVM vm, AIGroup *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIGroup", res, NULL, DefSQDestructorCallback<AIGroup>); return 1; }
+}; // namespace SQConvert
+
+void SQAIGroup_Register(Squirrel *engine) {
+ DefSQClass <AIGroup> SQAIGroup("AIGroup");
+ SQAIGroup.PreRegister(engine);
+ SQAIGroup.AddConstructor<void (AIGroup::*)(), 1>(engine, "x");
+
+ SQAIGroup.DefSQConst(engine, AIGroup::ALL_GROUP, "ALL_GROUP");
+ SQAIGroup.DefSQConst(engine, AIGroup::DEFAULT_GROUP, "DEFAULT_GROUP");
+ SQAIGroup.DefSQConst(engine, AIGroup::INVALID_GROUP, "INVALID_GROUP");
+
+ SQAIGroup.DefSQStaticMethod(engine, &AIGroup::GetClassName, "GetClassName", 1, "x");
+ SQAIGroup.DefSQStaticMethod(engine, &AIGroup::IsValidGroup, "IsValidGroup", 2, "xi");
+ SQAIGroup.DefSQStaticMethod(engine, &AIGroup::CreateGroup, "CreateGroup", 2, "xi");
+ SQAIGroup.DefSQStaticMethod(engine, &AIGroup::DeleteGroup, "DeleteGroup", 2, "xi");
+ SQAIGroup.DefSQStaticMethod(engine, &AIGroup::GetVehicleType, "GetVehicleType", 2, "xi");
+ SQAIGroup.DefSQStaticMethod(engine, &AIGroup::SetName, "SetName", 3, "xis");
+ SQAIGroup.DefSQStaticMethod(engine, &AIGroup::GetName, "GetName", 2, "xi");
+ SQAIGroup.DefSQStaticMethod(engine, &AIGroup::EnableAutoReplaceProtection, "EnableAutoReplaceProtection", 3, "xib");
+ SQAIGroup.DefSQStaticMethod(engine, &AIGroup::GetAutoReplaceProtection, "GetAutoReplaceProtection", 2, "xi");
+ SQAIGroup.DefSQStaticMethod(engine, &AIGroup::GetNumEngines, "GetNumEngines", 3, "xii");
+ SQAIGroup.DefSQStaticMethod(engine, &AIGroup::MoveVehicle, "MoveVehicle", 3, "xii");
+ SQAIGroup.DefSQStaticMethod(engine, &AIGroup::EnableWagonRemoval, "EnableWagonRemoval", 2, "xb");
+ SQAIGroup.DefSQStaticMethod(engine, &AIGroup::HasWagonRemoval, "HasWagonRemoval", 1, "x");
+ SQAIGroup.DefSQStaticMethod(engine, &AIGroup::SetAutoReplace, "SetAutoReplace", 4, "xiii");
+ SQAIGroup.DefSQStaticMethod(engine, &AIGroup::GetEngineReplacement, "GetEngineReplacement", 3, "xii");
+ SQAIGroup.DefSQStaticMethod(engine, &AIGroup::StopAutoReplace, "StopAutoReplace", 3, "xii");
+
+ SQAIGroup.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_grouplist.cpp b/src/ai/api/ai_grouplist.cpp
new file mode 100644
index 000000000..be57b3750
--- /dev/null
+++ b/src/ai/api/ai_grouplist.cpp
@@ -0,0 +1,16 @@
+/* $Id$ */
+
+/** @file ai_grouplist.cpp Implementation of AIGroupList and friends. */
+
+#include "ai_grouplist.hpp"
+#include "../../openttd.h"
+#include "../../company_func.h"
+#include "../../group.h"
+
+AIGroupList::AIGroupList()
+{
+ Group *g;
+ FOR_ALL_GROUPS(g) {
+ if (g->owner == _current_company) this->AddItem(g->index);
+ }
+}
diff --git a/src/ai/api/ai_grouplist.hpp b/src/ai/api/ai_grouplist.hpp
new file mode 100644
index 000000000..d97ef85c7
--- /dev/null
+++ b/src/ai/api/ai_grouplist.hpp
@@ -0,0 +1,21 @@
+/* $Id$ */
+
+/** @file ai_grouplist.hpp List all the groups (you own). */
+
+#ifndef AI_GROUPLIST_HPP
+#define AI_GROUPLIST_HPP
+
+#include "ai_abstractlist.hpp"
+
+/**
+ * Creates a list of groups of which you are the owner.
+ * @note Neither AIGroup.ALL_GROUP nor AIGroup.DEFAULT_GROUP is in this list.
+ * @ingroup AIList
+ */
+class AIGroupList : public AIAbstractList {
+public:
+ static const char *GetClassName() { return "AIGroupList"; }
+ AIGroupList();
+};
+
+#endif /* AI_GROUPLIST_HPP */
diff --git a/src/ai/api/ai_grouplist.hpp.sq b/src/ai/api/ai_grouplist.hpp.sq
new file mode 100644
index 000000000..b5c4d945d
--- /dev/null
+++ b/src/ai/api/ai_grouplist.hpp.sq
@@ -0,0 +1,23 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_grouplist.hpp"
+
+namespace SQConvert {
+ /* Allow AIGroupList to be used as Squirrel parameter */
+ template <> AIGroupList *GetParam(ForceType<AIGroupList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIGroupList *)instance; }
+ template <> AIGroupList &GetParam(ForceType<AIGroupList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIGroupList *)instance; }
+ template <> const AIGroupList *GetParam(ForceType<const AIGroupList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIGroupList *)instance; }
+ template <> const AIGroupList &GetParam(ForceType<const AIGroupList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIGroupList *)instance; }
+ template <> int Return<AIGroupList *>(HSQUIRRELVM vm, AIGroupList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIGroupList", res, NULL, DefSQDestructorCallback<AIGroupList>); return 1; }
+}; // namespace SQConvert
+
+void SQAIGroupList_Register(Squirrel *engine) {
+ DefSQClass <AIGroupList> SQAIGroupList("AIGroupList");
+ SQAIGroupList.PreRegister(engine, "AIAbstractList");
+ SQAIGroupList.AddConstructor<void (AIGroupList::*)(), 1>(engine, "x");
+
+ SQAIGroupList.DefSQStaticMethod(engine, &AIGroupList::GetClassName, "GetClassName", 1, "x");
+
+ SQAIGroupList.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_industry.cpp b/src/ai/api/ai_industry.cpp
new file mode 100644
index 000000000..64687af51
--- /dev/null
+++ b/src/ai/api/ai_industry.cpp
@@ -0,0 +1,170 @@
+/* $Id$ */
+
+/** @file ai_industry.cpp Implementation of AIIndustry. */
+
+#include "ai_industry.hpp"
+#include "ai_cargo.hpp"
+#include "ai_map.hpp"
+#include "../../openttd.h"
+#include "../../tile_type.h"
+#include "../../industry.h"
+#include "../../strings_func.h"
+#include "../../station_func.h"
+#include "table/strings.h"
+
+/* static */ IndustryID AIIndustry::GetMaxIndustryID()
+{
+ return ::GetMaxIndustryIndex();
+}
+
+/* static */ int32 AIIndustry::GetIndustryCount()
+{
+ return ::GetNumIndustries();
+}
+
+/* static */ bool AIIndustry::IsValidIndustry(IndustryID industry_id)
+{
+ return ::IsValidIndustryID(industry_id);
+}
+
+/* static */ const char *AIIndustry::GetName(IndustryID industry_id)
+{
+ if (!IsValidIndustry(industry_id)) return NULL;
+ static const int len = 64;
+ char *industry_name = MallocT<char>(len);
+
+ ::SetDParam(0, industry_id);
+ ::GetString(industry_name, STR_INDUSTRY, &industry_name[len - 1]);
+
+ return industry_name;
+}
+
+/* static */ int32 AIIndustry::GetProduction(IndustryID industry_id, CargoID cargo_id)
+{
+ if (!IsValidIndustry(industry_id)) return -1;
+ if (!AICargo::IsValidCargo(cargo_id)) return -1;
+
+ const Industry *i = ::GetIndustry(industry_id);
+ const IndustrySpec *indsp = ::GetIndustrySpec(i->type);
+
+ for (byte j = 0; j < lengthof(indsp->produced_cargo); j++)
+ if (indsp->produced_cargo[j] == cargo_id) return i->production_rate[j] * 8;
+
+ return -1;
+}
+
+/* static */ bool AIIndustry::IsCargoAccepted(IndustryID industry_id, CargoID cargo_id)
+{
+ if (!IsValidIndustry(industry_id)) return false;
+ if (!AICargo::IsValidCargo(cargo_id)) return false;
+
+ const Industry *i = ::GetIndustry(industry_id);
+ const IndustrySpec *indsp = ::GetIndustrySpec(i->type);
+
+ for (byte j = 0; j < lengthof(indsp->accepts_cargo); j++)
+ if (indsp->accepts_cargo[j] == cargo_id) return true;
+
+ return false;
+}
+
+/* static */ int32 AIIndustry::GetStockpiledCargo(IndustryID industry_id, CargoID cargo_id)
+{
+ if (!IsValidIndustry(industry_id)) return -1;
+ if (!AICargo::IsValidCargo(cargo_id)) return -1;
+
+ Industry *ind = ::GetIndustry(industry_id);
+ for (uint i = 0; i < lengthof(ind->accepts_cargo); i++) {
+ CargoID cid = ind->accepts_cargo[i];
+ if (cid == cargo_id) {
+ return ind->incoming_cargo_waiting[i];
+ }
+ }
+
+ return -1;
+}
+
+/* static */ int32 AIIndustry::GetLastMonthProduction(IndustryID industry_id, CargoID cargo_id)
+{
+ if (!IsValidIndustry(industry_id)) return -1;
+ if (!AICargo::IsValidCargo(cargo_id)) return -1;
+
+ const Industry *i = ::GetIndustry(industry_id);
+ const IndustrySpec *indsp = ::GetIndustrySpec(i->type);
+
+ for (byte j = 0; j < lengthof(indsp->produced_cargo); j++)
+ if (indsp->produced_cargo[j] == cargo_id) return i->last_month_production[j];
+
+ return -1;
+}
+
+/* static */ int32 AIIndustry::GetLastMonthTransported(IndustryID industry_id, CargoID cargo_id)
+{
+ if (!IsValidIndustry(industry_id)) return -1;
+ if (!AICargo::IsValidCargo(cargo_id)) return -1;
+
+ const Industry *i = ::GetIndustry(industry_id);
+ const IndustrySpec *indsp = ::GetIndustrySpec(i->type);
+
+ for (byte j = 0; j < lengthof(indsp->produced_cargo); j++)
+ if (indsp->produced_cargo[j] == cargo_id) return i->last_month_transported[j];
+
+ return -1;
+}
+
+/* static */ TileIndex AIIndustry::GetLocation(IndustryID industry_id)
+{
+ if (!IsValidIndustry(industry_id)) return INVALID_TILE;
+
+ return ::GetIndustry(industry_id)->xy;
+}
+
+/* static */ int32 AIIndustry::GetAmountOfStationsAround(IndustryID industry_id)
+{
+ if (!IsValidIndustry(industry_id)) return -1;
+
+ Industry *ind = ::GetIndustry(industry_id);
+ return (int32)::FindStationsAroundTiles(ind->xy, ind->width, ind->height).size();
+}
+
+/* static */ int32 AIIndustry::GetDistanceManhattanToTile(IndustryID industry_id, TileIndex tile)
+{
+ if (!IsValidIndustry(industry_id)) return -1;
+
+ return AIMap::DistanceManhattan(tile, GetLocation(industry_id));
+}
+
+/* static */ int32 AIIndustry::GetDistanceSquareToTile(IndustryID industry_id, TileIndex tile)
+{
+ if (!IsValidIndustry(industry_id)) return -1;
+
+ return AIMap::DistanceSquare(tile, GetLocation(industry_id));
+}
+
+/* static */ bool AIIndustry::IsBuiltOnWater(IndustryID industry_id)
+{
+ if (!IsValidIndustry(industry_id)) return false;
+
+ return (::GetIndustrySpec(::GetIndustry(industry_id)->type)->behaviour & INDUSTRYBEH_BUILT_ONWATER) != 0;
+}
+
+/* static */ bool AIIndustry::HasHeliportAndDock(IndustryID industry_id)
+{
+ if (!IsValidIndustry(industry_id)) return false;
+
+ return (::GetIndustrySpec(::GetIndustry(industry_id)->type)->behaviour & INDUSTRYBEH_AI_AIRSHIP_ROUTES) != 0;
+}
+
+/* static */ TileIndex AIIndustry::GetHeliportAndDockLocation(IndustryID industry_id)
+{
+ if (!IsValidIndustry(industry_id)) return INVALID_TILE;
+ if (!HasHeliportAndDock(industry_id)) return INVALID_TILE;
+
+ return ::GetIndustry(industry_id)->xy;
+}
+
+/* static */ IndustryType AIIndustry::GetIndustryType(IndustryID industry_id)
+{
+ if (!IsValidIndustry(industry_id)) return INVALID_INDUSTRYTYPE;
+
+ return ::GetIndustry(industry_id)->type;
+}
diff --git a/src/ai/api/ai_industry.hpp b/src/ai/api/ai_industry.hpp
new file mode 100644
index 000000000..1b3365a97
--- /dev/null
+++ b/src/ai/api/ai_industry.hpp
@@ -0,0 +1,171 @@
+/* $Id$ */
+
+/** @file ai_industry.hpp Everything to query and build industries. */
+
+#ifndef AI_INDUSTRY_HPP
+#define AI_INDUSTRY_HPP
+
+#include "ai_object.hpp"
+
+/**
+ * Class that handles all industry related functions.
+ */
+class AIIndustry : public AIObject {
+public:
+ static const char *GetClassName() { return "AIIndustry"; }
+
+ /**
+ * Gets the maximum industry index; there are no valid industries with a
+ * higher index.
+ * @return The maximum industry index.
+ * @post Return value is always non-negative.
+ */
+ static IndustryID GetMaxIndustryID();
+
+ /**
+ * Gets the number of industries. This is different than GetMaxIndustryID()
+ * because of the way OpenTTD works internally.
+ * @return The number of industries.
+ * @post Return value is always non-negative.
+ */
+ static int32 GetIndustryCount();
+
+ /**
+ * Checks whether the given industry index is valid.
+ * @param industry_id The index to check.
+ * @return True if and only if the industry is valid.
+ */
+ static bool IsValidIndustry(IndustryID industry_id);
+
+ /**
+ * Get the name of the industry.
+ * @param industry_id The industry to get the name of.
+ * @pre IsValidIndustry(industry_id).
+ * @return The name of the industry.
+ */
+ static const char *GetName(IndustryID industry_id);
+
+ /**
+ * Gets the production of a cargo of the industry.
+ * @param industry_id The index of the industry.
+ * @param cargo_id The index of the cargo.
+ * @pre IsValidIndustry(industry_id).
+ * @pre AICargo::IsValidCargo(cargo_id).
+ * @return The production of the cargo for this industry, or -1 if
+ * this industry doesn't produce this cargo.
+ */
+ static int32 GetProduction(IndustryID industry_id, CargoID cargo_id);
+
+ /**
+ * See if an industry accepts a certain cargo.
+ * @param industry_id The index of the industry.
+ * @param cargo_id The index of the cargo.
+ * @pre IsValidIndustry(industry_id).
+ * @pre AICargo::IsValidCargo(cargo_id).
+ * @return The production of the cargo for this industry.
+ */
+ static bool IsCargoAccepted(IndustryID industry_id, CargoID cargo_id);
+
+ /**
+ * Get the amount of cargo stockpiled for processing.
+ * @param industry_id The index of the industry.
+ * @param cargo_id The index of the cargo.
+ * @pre IsValidIndustry(industry_id).
+ * @pre AICargo::IsValidCargo(cargo_id).
+ * @return The amount of cargo that is waiting for processing.
+ */
+ static int32 GetStockpiledCargo(IndustryID industry_id, CargoID cargo_id);
+
+ /**
+ * Get the total last month's production of the given cargo at an industry.
+ * @param industry_id The index of the industry.
+ * @param cargo_id The index of the cargo.
+ * @pre IsValidIndustry(industry_id).
+ * @pre AICargo::IsValidCargo(cargo_id).
+ * @return The last month's production of the given cargo for this industry.
+ */
+ static int32 GetLastMonthProduction(IndustryID industry_id, CargoID cargo_id);
+
+ /**
+ * Get the total amount of cargo transported from an industry last month.
+ * @param industry_id The index of the industry.
+ * @param cargo_id The index of the cargo.
+ * @pre IsValidIndustry(industry_id).
+ * @pre AICargo::IsValidCargo(cargo_id).
+ * @return The amount of given cargo transported from this industry last month.
+ */
+ static int32 GetLastMonthTransported(IndustryID industry_id, CargoID cargo_id);
+
+ /**
+ * Gets the location of the industry.
+ * @param industry_id The index of the industry.
+ * @pre IsValidIndustry(industry_id).
+ * @return The location of the industry.
+ */
+ static TileIndex GetLocation(IndustryID industry_id);
+
+ /**
+ * Get the number of stations around an industry.
+ * @param industry_id The index of the industry.
+ * @pre IsValidIndustry(industry_id).
+ * @return The number of stations around an industry.
+ */
+ static int32 GetAmountOfStationsAround(IndustryID industry_id);
+
+ /**
+ * Get the manhattan distance from the tile to the AIIndustry::GetLocation()
+ * of the industry.
+ * @param industry_id The industry to get the distance to.
+ * @param tile The tile to get the distance to.
+ * @pre IsValidIndustry(industry_id).
+ * @pre AIMap::IsValidTile(tile).
+ * @return The distance between industry and tile.
+ */
+ static int32 GetDistanceManhattanToTile(IndustryID industry_id, TileIndex tile);
+
+ /**
+ * Get the square distance from the tile to the AIIndustry::GetLocation()
+ * of the industry.
+ * @param industry_id The industry to get the distance to.
+ * @param tile The tile to get the distance to.
+ * @pre IsValidIndustry(industry_id).
+ * @pre AIMap::IsValidTile(tile).
+ * @return The distance between industry and tile.
+ */
+ static int32 GetDistanceSquareToTile(IndustryID industry_id, TileIndex tile);
+
+ /**
+ * Is this industry built on water.
+ * @param industry_id The index of the industry.
+ * @pre IsValidIndustry(industry_id).
+ * @return True when the industry is built on water.
+ */
+ static bool IsBuiltOnWater(IndustryID industry_id);
+
+ /**
+ * Does this industry have a heliport and dock?
+ * @param industry_id The index of the industry.
+ * @pre IsValidIndustry(industry_id).
+ * @return True when the industry has a heliport and dock.
+ */
+ static bool HasHeliportAndDock(IndustryID industry_id);
+
+ /**
+ * Gets the location of the industry's heliport/dock.
+ * @param industry_id The index of the industry.
+ * @pre IsValidIndustry(industry_id).
+ * @pre HasHeliportAndDock(industry_id).
+ * @return The location of the industry's heliport/dock.
+ */
+ static TileIndex GetHeliportAndDockLocation(IndustryID industry_id);
+
+ /**
+ * Get the IndustryType of the industry.
+ * @param industry_id The index of the industry.
+ * @pre IsValidIndustry(industry_id).
+ * @return The IndustryType of the industry.
+ */
+ static IndustryType GetIndustryType(IndustryID industry_id);
+};
+
+#endif /* AI_INDUSTRY_HPP */
diff --git a/src/ai/api/ai_industry.hpp.sq b/src/ai/api/ai_industry.hpp.sq
new file mode 100644
index 000000000..dc0493efb
--- /dev/null
+++ b/src/ai/api/ai_industry.hpp.sq
@@ -0,0 +1,40 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_industry.hpp"
+
+namespace SQConvert {
+ /* Allow AIIndustry to be used as Squirrel parameter */
+ template <> AIIndustry *GetParam(ForceType<AIIndustry *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustry *)instance; }
+ template <> AIIndustry &GetParam(ForceType<AIIndustry &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustry *)instance; }
+ template <> const AIIndustry *GetParam(ForceType<const AIIndustry *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustry *)instance; }
+ template <> const AIIndustry &GetParam(ForceType<const AIIndustry &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustry *)instance; }
+ template <> int Return<AIIndustry *>(HSQUIRRELVM vm, AIIndustry *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIIndustry", res, NULL, DefSQDestructorCallback<AIIndustry>); return 1; }
+}; // namespace SQConvert
+
+void SQAIIndustry_Register(Squirrel *engine) {
+ DefSQClass <AIIndustry> SQAIIndustry("AIIndustry");
+ SQAIIndustry.PreRegister(engine);
+ SQAIIndustry.AddConstructor<void (AIIndustry::*)(), 1>(engine, "x");
+
+ SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetClassName, "GetClassName", 1, "x");
+ SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetMaxIndustryID, "GetMaxIndustryID", 1, "x");
+ SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetIndustryCount, "GetIndustryCount", 1, "x");
+ SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::IsValidIndustry, "IsValidIndustry", 2, "xi");
+ SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetName, "GetName", 2, "xi");
+ SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetProduction, "GetProduction", 3, "xii");
+ SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::IsCargoAccepted, "IsCargoAccepted", 3, "xii");
+ SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetStockpiledCargo, "GetStockpiledCargo", 3, "xii");
+ SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetLastMonthProduction, "GetLastMonthProduction", 3, "xii");
+ SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetLastMonthTransported, "GetLastMonthTransported", 3, "xii");
+ SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetLocation, "GetLocation", 2, "xi");
+ SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetAmountOfStationsAround, "GetAmountOfStationsAround", 2, "xi");
+ SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetDistanceManhattanToTile, "GetDistanceManhattanToTile", 3, "xii");
+ SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetDistanceSquareToTile, "GetDistanceSquareToTile", 3, "xii");
+ SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::IsBuiltOnWater, "IsBuiltOnWater", 2, "xi");
+ SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::HasHeliportAndDock, "HasHeliportAndDock", 2, "xi");
+ SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetHeliportAndDockLocation, "GetHeliportAndDockLocation", 2, "xi");
+ SQAIIndustry.DefSQStaticMethod(engine, &AIIndustry::GetIndustryType, "GetIndustryType", 2, "xi");
+
+ SQAIIndustry.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_industrylist.cpp b/src/ai/api/ai_industrylist.cpp
new file mode 100644
index 000000000..be8fbcfa4
--- /dev/null
+++ b/src/ai/api/ai_industrylist.cpp
@@ -0,0 +1,42 @@
+/* $Id$ */
+
+/** @file ai_industrylist.cpp Implementation of AIIndustryList and friends. */
+
+#include "ai_industrylist.hpp"
+#include "../../openttd.h"
+#include "../../tile_type.h"
+#include "../../industry.h"
+
+AIIndustryList::AIIndustryList()
+{
+ Industry *i;
+ FOR_ALL_INDUSTRIES(i) {
+ this->AddItem(i->index);
+ }
+}
+
+AIIndustryList_CargoAccepting::AIIndustryList_CargoAccepting(CargoID cargo_id)
+{
+ const Industry *i;
+ const IndustrySpec *indsp;
+
+ FOR_ALL_INDUSTRIES(i) {
+ indsp = ::GetIndustrySpec(i->type);
+
+ for (byte j = 0; j < lengthof(indsp->accepts_cargo); j++)
+ if (indsp->accepts_cargo[j] == cargo_id) this->AddItem(i->index);
+ }
+}
+
+AIIndustryList_CargoProducing::AIIndustryList_CargoProducing(CargoID cargo_id)
+{
+ const Industry *i;
+ const IndustrySpec *indsp;
+
+ FOR_ALL_INDUSTRIES(i) {
+ indsp = ::GetIndustrySpec(i->type);
+
+ for (byte j = 0; j < lengthof(indsp->produced_cargo); j++)
+ if (indsp->produced_cargo[j] == cargo_id) this->AddItem(i->index);
+ }
+}
diff --git a/src/ai/api/ai_industrylist.hpp b/src/ai/api/ai_industrylist.hpp
new file mode 100644
index 000000000..2afca7b97
--- /dev/null
+++ b/src/ai/api/ai_industrylist.hpp
@@ -0,0 +1,49 @@
+/* $Id$ */
+
+/** @file ai_industrylist.hpp List all the industries. */
+
+#ifndef AI_INDUSTRYLIST_HPP
+#define AI_INDUSTRYLIST_HPP
+
+#include "ai_abstractlist.hpp"
+
+/**
+ * Creates a list of industries that are currently on the map.
+ * @ingroup AIList
+ */
+class AIIndustryList : public AIAbstractList {
+public:
+ static const char *GetClassName() { return "AIIndustryList"; }
+ AIIndustryList();
+};
+
+/**
+ * Creates a list of industries that accepts a given cargo.
+ * @ingroup AIList
+ */
+class AIIndustryList_CargoAccepting : public AIAbstractList {
+public:
+ static const char *GetClassName() { return "AIIndustryList_CargoAccepting"; }
+
+ /**
+ * @param cargo_id The cargo this industry should accept.
+ */
+ AIIndustryList_CargoAccepting(CargoID cargo_id);
+};
+
+/**
+ * Creates a list of industries that can produce a given cargo.
+ * @note It also contains industries that currently produces 0 units of the cargo.
+ * @ingroup AIList
+ */
+class AIIndustryList_CargoProducing : public AIAbstractList {
+public:
+ static const char *GetClassName() { return "AIIndustryList_CargoProducing"; }
+
+ /**
+ * @param cargo_id The cargo this industry should produce.
+ */
+ AIIndustryList_CargoProducing(CargoID cargo_id);
+};
+
+#endif /* AI_INDUSTRYLIST_HPP */
diff --git a/src/ai/api/ai_industrylist.hpp.sq b/src/ai/api/ai_industrylist.hpp.sq
new file mode 100644
index 000000000..1abc9c549
--- /dev/null
+++ b/src/ai/api/ai_industrylist.hpp.sq
@@ -0,0 +1,61 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_industrylist.hpp"
+
+namespace SQConvert {
+ /* Allow AIIndustryList to be used as Squirrel parameter */
+ template <> AIIndustryList *GetParam(ForceType<AIIndustryList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustryList *)instance; }
+ template <> AIIndustryList &GetParam(ForceType<AIIndustryList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustryList *)instance; }
+ template <> const AIIndustryList *GetParam(ForceType<const AIIndustryList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustryList *)instance; }
+ template <> const AIIndustryList &GetParam(ForceType<const AIIndustryList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustryList *)instance; }
+ template <> int Return<AIIndustryList *>(HSQUIRRELVM vm, AIIndustryList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIIndustryList", res, NULL, DefSQDestructorCallback<AIIndustryList>); return 1; }
+}; // namespace SQConvert
+
+void SQAIIndustryList_Register(Squirrel *engine) {
+ DefSQClass <AIIndustryList> SQAIIndustryList("AIIndustryList");
+ SQAIIndustryList.PreRegister(engine, "AIAbstractList");
+ SQAIIndustryList.AddConstructor<void (AIIndustryList::*)(), 1>(engine, "x");
+
+ SQAIIndustryList.DefSQStaticMethod(engine, &AIIndustryList::GetClassName, "GetClassName", 1, "x");
+
+ SQAIIndustryList.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AIIndustryList_CargoAccepting to be used as Squirrel parameter */
+ template <> AIIndustryList_CargoAccepting *GetParam(ForceType<AIIndustryList_CargoAccepting *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustryList_CargoAccepting *)instance; }
+ template <> AIIndustryList_CargoAccepting &GetParam(ForceType<AIIndustryList_CargoAccepting &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustryList_CargoAccepting *)instance; }
+ template <> const AIIndustryList_CargoAccepting *GetParam(ForceType<const AIIndustryList_CargoAccepting *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustryList_CargoAccepting *)instance; }
+ template <> const AIIndustryList_CargoAccepting &GetParam(ForceType<const AIIndustryList_CargoAccepting &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustryList_CargoAccepting *)instance; }
+ template <> int Return<AIIndustryList_CargoAccepting *>(HSQUIRRELVM vm, AIIndustryList_CargoAccepting *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIIndustryList_CargoAccepting", res, NULL, DefSQDestructorCallback<AIIndustryList_CargoAccepting>); return 1; }
+}; // namespace SQConvert
+
+void SQAIIndustryList_CargoAccepting_Register(Squirrel *engine) {
+ DefSQClass <AIIndustryList_CargoAccepting> SQAIIndustryList_CargoAccepting("AIIndustryList_CargoAccepting");
+ SQAIIndustryList_CargoAccepting.PreRegister(engine, "AIAbstractList");
+ SQAIIndustryList_CargoAccepting.AddConstructor<void (AIIndustryList_CargoAccepting::*)(CargoID cargo_id), 2>(engine, "xi");
+
+ SQAIIndustryList_CargoAccepting.DefSQStaticMethod(engine, &AIIndustryList_CargoAccepting::GetClassName, "GetClassName", 1, "x");
+
+ SQAIIndustryList_CargoAccepting.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AIIndustryList_CargoProducing to be used as Squirrel parameter */
+ template <> AIIndustryList_CargoProducing *GetParam(ForceType<AIIndustryList_CargoProducing *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustryList_CargoProducing *)instance; }
+ template <> AIIndustryList_CargoProducing &GetParam(ForceType<AIIndustryList_CargoProducing &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustryList_CargoProducing *)instance; }
+ template <> const AIIndustryList_CargoProducing *GetParam(ForceType<const AIIndustryList_CargoProducing *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustryList_CargoProducing *)instance; }
+ template <> const AIIndustryList_CargoProducing &GetParam(ForceType<const AIIndustryList_CargoProducing &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustryList_CargoProducing *)instance; }
+ template <> int Return<AIIndustryList_CargoProducing *>(HSQUIRRELVM vm, AIIndustryList_CargoProducing *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIIndustryList_CargoProducing", res, NULL, DefSQDestructorCallback<AIIndustryList_CargoProducing>); return 1; }
+}; // namespace SQConvert
+
+void SQAIIndustryList_CargoProducing_Register(Squirrel *engine) {
+ DefSQClass <AIIndustryList_CargoProducing> SQAIIndustryList_CargoProducing("AIIndustryList_CargoProducing");
+ SQAIIndustryList_CargoProducing.PreRegister(engine, "AIAbstractList");
+ SQAIIndustryList_CargoProducing.AddConstructor<void (AIIndustryList_CargoProducing::*)(CargoID cargo_id), 2>(engine, "xi");
+
+ SQAIIndustryList_CargoProducing.DefSQStaticMethod(engine, &AIIndustryList_CargoProducing::GetClassName, "GetClassName", 1, "x");
+
+ SQAIIndustryList_CargoProducing.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_industrytype.cpp b/src/ai/api/ai_industrytype.cpp
new file mode 100644
index 000000000..34d69f5f8
--- /dev/null
+++ b/src/ai/api/ai_industrytype.cpp
@@ -0,0 +1,115 @@
+/* $Id$ */
+
+/** @file ai_industrytype.cpp Implementation of AIIndustryType. */
+
+#include "ai_industrytype.hpp"
+#include "ai_map.hpp"
+#include "../../openttd.h"
+#include "../../command_type.h"
+#include "../../settings_type.h"
+#include "../../strings_func.h"
+#include "../../tile_type.h"
+#include "../../industry.h"
+
+/* static */ bool AIIndustryType::IsValidIndustryType(IndustryType industry_type)
+{
+ if (industry_type >= NUM_INDUSTRYTYPES) return false;
+
+ return ::GetIndustrySpec(industry_type)->enabled;
+}
+
+/* static */ bool AIIndustryType::IsRawIndustry(IndustryType industry_type)
+{
+ if (!IsValidIndustryType(industry_type)) return false;
+
+ return ::GetIndustrySpec(industry_type)->IsRawIndustry();
+}
+
+/* static */ bool AIIndustryType::ProductionCanIncrease(IndustryType industry_type)
+{
+ if (!IsValidIndustryType(industry_type)) return false;
+
+ if (_settings_game.game_creation.landscape != LT_TEMPERATE) return true;
+ return (::GetIndustrySpec(industry_type)->behaviour & INDUSTRYBEH_DONT_INCR_PROD) == 0;
+}
+
+/* static */ Money AIIndustryType::GetConstructionCost(IndustryType industry_type)
+{
+ if (!IsValidIndustryType(industry_type)) return false;
+
+ return ::GetIndustrySpec(industry_type)->GetConstructionCost();
+}
+
+/* static */ const char *AIIndustryType::GetName(IndustryType industry_type)
+{
+ if (!IsValidIndustryType(industry_type)) return NULL;
+ static const int len = 64;
+ char *industrytype_name = MallocT<char>(len);
+
+ ::GetString(industrytype_name, ::GetIndustrySpec(industry_type)->name, &industrytype_name[len - 1]);
+
+ return industrytype_name;
+}
+
+/* static */ AIList *AIIndustryType::GetProducedCargo(IndustryType industry_type)
+{
+ if (!IsValidIndustryType(industry_type)) return NULL;
+
+ const IndustrySpec *ins = ::GetIndustrySpec(industry_type);
+
+ AIList *list = new AIList();
+ for (int i = 0; i < 2; i++) {
+ if (ins->produced_cargo[i] != CT_INVALID) list->AddItem(ins->produced_cargo[i], 0);
+ }
+
+ return list;
+}
+
+/* static */ AIList *AIIndustryType::GetAcceptedCargo(IndustryType industry_type)
+{
+ if (!IsValidIndustryType(industry_type)) return NULL;
+
+ const IndustrySpec *ins = ::GetIndustrySpec(industry_type);
+
+ AIList *list = new AIList();
+ for (int i = 0; i < 3; i++) {
+ if (ins->accepts_cargo[i] != CT_INVALID) list->AddItem(ins->accepts_cargo[i], 0);
+ }
+
+ return list;
+}
+
+/* static */ bool AIIndustryType::CanBuildIndustry(IndustryType industry_type)
+{
+ if (!IsValidIndustryType(industry_type)) return false;
+ if (!::GetIndustrySpec(industry_type)->IsRawIndustry()) return true;
+
+ /* raw_industry_construction == 1 means "Build as other industries" */
+ return _settings_game.construction.raw_industry_construction == 1;
+}
+
+/* static */ bool AIIndustryType::CanProspectIndustry(IndustryType industry_type)
+{
+ if (!IsValidIndustryType(industry_type)) return false;
+ if (!::GetIndustrySpec(industry_type)->IsRawIndustry()) return false;
+
+ /* raw_industry_construction == 2 means "prospect" */
+ return _settings_game.construction.raw_industry_construction == 2;
+}
+
+/* static */ bool AIIndustryType::BuildIndustry(IndustryType industry_type, TileIndex tile)
+{
+ EnforcePrecondition(false, CanBuildIndustry(industry_type));
+ EnforcePrecondition(false, AIMap::IsValidTile(tile));
+
+ uint32 seed = ::InteractiveRandom();
+ return AIObject::DoCommand(tile, (::InteractiveRandomRange(::GetIndustrySpec(industry_type)->num_table) << 16) | industry_type, seed, CMD_BUILD_INDUSTRY);
+}
+
+/* static */ bool AIIndustryType::ProspectIndustry(IndustryType industry_type)
+{
+ EnforcePrecondition(false, CanProspectIndustry(industry_type));
+
+ uint32 seed = ::InteractiveRandom();
+ return AIObject::DoCommand(0, industry_type, seed, CMD_BUILD_INDUSTRY);
+}
diff --git a/src/ai/api/ai_industrytype.hpp b/src/ai/api/ai_industrytype.hpp
new file mode 100644
index 000000000..88ba6d730
--- /dev/null
+++ b/src/ai/api/ai_industrytype.hpp
@@ -0,0 +1,114 @@
+/* $Id$ */
+
+/** @file ai_industrytype.hpp Everything to query and build industries. */
+
+#ifndef AI_INDUSTRYTYPE_HPP
+#define AI_INDUSTRYTYPE_HPP
+
+#include "ai_object.hpp"
+#include "ai_error.hpp"
+#include "ai_list.hpp"
+
+/**
+ * Class that handles all industry-type related functions.
+ */
+class AIIndustryType : public AIObject {
+public:
+ static const char *GetClassName() { return "AIIndustryType"; }
+
+ /**
+ * Checks whether the given industry-type is valid.
+ * @param industry_type The type check.
+ * @return True if and only if the industry-type is valid.
+ */
+ static bool IsValidIndustryType(IndustryType industry_type);
+
+ /**
+ * Get the name of an industry-type.
+ * @param industry_type The type to get the name for.
+ * @pre IsValidIndustryType(industry_type).
+ * @return The name of an industry.
+ */
+ static const char *GetName(IndustryType industry_type);
+
+ /**
+ * Get a list of CargoID possible produced by this industry-type.
+ * @param industry_type The type to get the CargoIDs for.
+ * @pre IsValidIndustryType(industry_type).
+ * @return The CargoIDs of all cargotypes this industry could produce.
+ */
+ static AIList *GetProducedCargo(IndustryType industry_type);
+
+ /**
+ * Get a list of CargoID accepted by this industry-type.
+ * @param industry_type The type to get the CargoIDs for.
+ * @pre IsValidIndustryType(industry_type).
+ * @return The CargoIDs of all cargotypes this industry accepts.
+ */
+ static AIList *GetAcceptedCargo(IndustryType industry_type);
+
+ /**
+ * Is this industry type a raw industry?
+ * @param industry_type The type of the industry.
+ * @pre IsValidIndustryType(industry_type).
+ * @return True if it should be handled as a raw industry.
+ */
+ static bool IsRawIndustry(IndustryType industry_type);
+
+ /**
+ * Can the production of this industry increase?
+ * @param industry_type The type of the industry.
+ * @pre IsValidIndustryType(industry_type).
+ * @return True if the production of this industry can increase.
+ */
+ static bool ProductionCanIncrease(IndustryType industry_type);
+
+ /**
+ * Get the cost for building this industry-type.
+ * @param industry_type The type of the industry.
+ * @pre IsValidIndustryType(industry_type).
+ * @return The cost for building this industry-type.
+ */
+ static Money GetConstructionCost(IndustryType industry_type);
+
+ /**
+ * Can you build this type of industry?
+ * @param industry_type The type of the industry.
+ * @pre IsValidIndustryType(industry_type).
+ * @return True if you can prospect this type of industry.
+ * @note Returns false if you can only prospect this type of industry.
+ */
+ static bool CanBuildIndustry(IndustryType industry_type);
+
+ /**
+ * Can you prospect this type of industry?
+ * @param industry_type The type of the industry.
+ * @pre IsValidIndustryType(industry_type).
+ * @return True if you can prospect this type of industry.
+ * @note If the patch setting "Manual primary industry construction method" is set
+ * to either "None" or "as other industries" this function always returns false.
+ */
+ static bool CanProspectIndustry(IndustryType industry_type);
+
+ /**
+ * Build an industry of the specified type.
+ * @param industry_type The type of the industry to build.
+ * @param tile The tile to build the industry on.
+ * @pre CanBuildIndustry(industry_type).
+ * @return True if the industry was succesfully build.
+ */
+ static bool BuildIndustry(IndustryType industry_type, TileIndex tile);
+
+ /**
+ * Prospect an industry of this type. Prospecting an industries let the game try to create
+ * an industry on a random place on the map.
+ * @param industry_type The type of the industry.
+ * @pre CanProspectIndustry(industry_type).
+ * @return True if no error occured while trying to prospect.
+ * @note Even if true is returned there is no guarantee a new industry is build.
+ * @note If true is returned the money is paid, whether a new industry was build or not.
+ */
+ static bool ProspectIndustry(IndustryType industry_type);
+};
+
+#endif /* AI_INDUSTRYTYPE_HPP */
diff --git a/src/ai/api/ai_industrytype.hpp.sq b/src/ai/api/ai_industrytype.hpp.sq
new file mode 100644
index 000000000..ed5353776
--- /dev/null
+++ b/src/ai/api/ai_industrytype.hpp.sq
@@ -0,0 +1,34 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_industrytype.hpp"
+
+namespace SQConvert {
+ /* Allow AIIndustryType to be used as Squirrel parameter */
+ template <> AIIndustryType *GetParam(ForceType<AIIndustryType *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustryType *)instance; }
+ template <> AIIndustryType &GetParam(ForceType<AIIndustryType &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustryType *)instance; }
+ template <> const AIIndustryType *GetParam(ForceType<const AIIndustryType *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustryType *)instance; }
+ template <> const AIIndustryType &GetParam(ForceType<const AIIndustryType &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustryType *)instance; }
+ template <> int Return<AIIndustryType *>(HSQUIRRELVM vm, AIIndustryType *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIIndustryType", res, NULL, DefSQDestructorCallback<AIIndustryType>); return 1; }
+}; // namespace SQConvert
+
+void SQAIIndustryType_Register(Squirrel *engine) {
+ DefSQClass <AIIndustryType> SQAIIndustryType("AIIndustryType");
+ SQAIIndustryType.PreRegister(engine);
+ SQAIIndustryType.AddConstructor<void (AIIndustryType::*)(), 1>(engine, "x");
+
+ SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::GetClassName, "GetClassName", 1, "x");
+ SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::IsValidIndustryType, "IsValidIndustryType", 2, "xi");
+ SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::GetName, "GetName", 2, "xi");
+ SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::GetProducedCargo, "GetProducedCargo", 2, "xi");
+ SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::GetAcceptedCargo, "GetAcceptedCargo", 2, "xi");
+ SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::IsRawIndustry, "IsRawIndustry", 2, "xi");
+ SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::ProductionCanIncrease, "ProductionCanIncrease", 2, "xi");
+ SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::GetConstructionCost, "GetConstructionCost", 2, "xi");
+ SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::CanBuildIndustry, "CanBuildIndustry", 2, "xi");
+ SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::CanProspectIndustry, "CanProspectIndustry", 2, "xi");
+ SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::BuildIndustry, "BuildIndustry", 3, "xii");
+ SQAIIndustryType.DefSQStaticMethod(engine, &AIIndustryType::ProspectIndustry, "ProspectIndustry", 2, "xi");
+
+ SQAIIndustryType.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_industrytypelist.cpp b/src/ai/api/ai_industrytypelist.cpp
new file mode 100644
index 000000000..b5ef34b50
--- /dev/null
+++ b/src/ai/api/ai_industrytypelist.cpp
@@ -0,0 +1,15 @@
+/* $Id$ */
+
+/** @file ai_industrytypelist.cpp Implementation of AIIndustryTypeList. */
+
+#include "ai_industrytypelist.hpp"
+#include "../../openttd.h"
+#include "../../tile_type.h"
+#include "../../industry.h"
+
+AIIndustryTypeList::AIIndustryTypeList()
+{
+ for (int i = 0; i < NUM_INDUSTRYTYPES; i++) {
+ if (AIIndustryType::IsValidIndustryType(i)) this->AddItem(i);
+ }
+}
diff --git a/src/ai/api/ai_industrytypelist.hpp b/src/ai/api/ai_industrytypelist.hpp
new file mode 100644
index 000000000..55c582c9d
--- /dev/null
+++ b/src/ai/api/ai_industrytypelist.hpp
@@ -0,0 +1,22 @@
+/* $Id$ */
+
+/** @file ai_industrytypelist.hpp List all available industry types. */
+
+#ifndef AI_INDUSTRYTYPELIST_HPP
+#define AI_INDUSTRYTYPELIST_HPP
+
+#include "ai_abstractlist.hpp"
+#include "ai_industrytype.hpp"
+
+/**
+ * Creates a list of valid industry types.
+ * @ingroup AIList
+ */
+class AIIndustryTypeList : public AIAbstractList {
+public:
+ static const char *GetClassName() { return "AIIndustryTypeList"; }
+ AIIndustryTypeList();
+};
+
+
+#endif /* AI_INDUSTRYTYPELIST_HPP */
diff --git a/src/ai/api/ai_industrytypelist.hpp.sq b/src/ai/api/ai_industrytypelist.hpp.sq
new file mode 100644
index 000000000..901ee4f68
--- /dev/null
+++ b/src/ai/api/ai_industrytypelist.hpp.sq
@@ -0,0 +1,23 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_industrytypelist.hpp"
+
+namespace SQConvert {
+ /* Allow AIIndustryTypeList to be used as Squirrel parameter */
+ template <> AIIndustryTypeList *GetParam(ForceType<AIIndustryTypeList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustryTypeList *)instance; }
+ template <> AIIndustryTypeList &GetParam(ForceType<AIIndustryTypeList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustryTypeList *)instance; }
+ template <> const AIIndustryTypeList *GetParam(ForceType<const AIIndustryTypeList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIIndustryTypeList *)instance; }
+ template <> const AIIndustryTypeList &GetParam(ForceType<const AIIndustryTypeList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIIndustryTypeList *)instance; }
+ template <> int Return<AIIndustryTypeList *>(HSQUIRRELVM vm, AIIndustryTypeList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIIndustryTypeList", res, NULL, DefSQDestructorCallback<AIIndustryTypeList>); return 1; }
+}; // namespace SQConvert
+
+void SQAIIndustryTypeList_Register(Squirrel *engine) {
+ DefSQClass <AIIndustryTypeList> SQAIIndustryTypeList("AIIndustryTypeList");
+ SQAIIndustryTypeList.PreRegister(engine, "AIAbstractList");
+ SQAIIndustryTypeList.AddConstructor<void (AIIndustryTypeList::*)(), 1>(engine, "x");
+
+ SQAIIndustryTypeList.DefSQStaticMethod(engine, &AIIndustryTypeList::GetClassName, "GetClassName", 1, "x");
+
+ SQAIIndustryTypeList.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_list.cpp b/src/ai/api/ai_list.cpp
new file mode 100644
index 000000000..57690d341
--- /dev/null
+++ b/src/ai/api/ai_list.cpp
@@ -0,0 +1,45 @@
+/* $Id$ */
+
+/** @file ai_list.cpp Implementation of AIList. */
+
+#include <squirrel.h>
+#include "ai_list.hpp"
+
+void AIList::AddItem(int32 item, int32 value)
+{
+ AIAbstractList::AddItem(item);
+ this->SetValue(item, value);
+}
+
+void AIList::ChangeItem(int32 item, int32 value)
+{
+ this->SetValue(item, value);
+}
+
+void AIList::RemoveItem(int32 item)
+{
+ AIAbstractList::RemoveItem(item);
+}
+
+SQInteger AIList::_set(HSQUIRRELVM vm) {
+ if (sq_gettype(vm, 2) != OT_INTEGER) return SQ_ERROR;
+ if (sq_gettype(vm, 3) != OT_INTEGER || sq_gettype(vm, 3) == OT_NULL) {
+ return sq_throwerror(vm, _SC("you can only assign integers to this list"));
+ }
+
+ SQInteger idx, val;
+ sq_getinteger(vm, 2, &idx);
+ if (sq_gettype(vm, 3) == OT_NULL) {
+ this->RemoveItem(idx);
+ return 0;
+ }
+
+ sq_getinteger(vm, 3, &val);
+ if (!this->HasItem(idx)) {
+ this->AddItem(idx, val);
+ return 0;
+ }
+
+ this->ChangeItem(idx, val);
+ return 0;
+}
diff --git a/src/ai/api/ai_list.hpp b/src/ai/api/ai_list.hpp
new file mode 100644
index 000000000..9ce99a8e0
--- /dev/null
+++ b/src/ai/api/ai_list.hpp
@@ -0,0 +1,47 @@
+/* $Id$ */
+
+/** @file ai_list.hpp List custom entries. */
+
+#ifndef AI_LIST_HPP
+#define AI_LIST_HPP
+
+#include "ai_abstractlist.hpp"
+
+/**
+ * Creates an empty list, in which you can add integers.
+ * @ingroup AIList
+ */
+class AIList : public AIAbstractList {
+public:
+ static const char *GetClassName() { return "AIList"; }
+
+public:
+ /**
+ * Add an item to the list.
+ * @param item the item to add.
+ * @param value the value to assign.
+ */
+ void AddItem(int32 item, int32 value);
+
+ /**
+ * Change the value of an item in the list.
+ * @param item the item to change
+ * @param value the value to assign.
+ */
+ void ChangeItem(int32 item, int32 value);
+
+ /**
+ * Remove the item from the list.
+ * @param item the item to remove.
+ */
+ void RemoveItem(int32 item);
+
+#ifndef DOXYGEN_SKIP
+ /**
+ * Used for [] set from Squirrel.
+ */
+ SQInteger _set(HSQUIRRELVM vm);
+#endif /* DOXYGEN_SKIP */
+};
+
+#endif /* AI_LIST_HPP */
diff --git a/src/ai/api/ai_list.hpp.sq b/src/ai/api/ai_list.hpp.sq
new file mode 100644
index 000000000..dfa270bbf
--- /dev/null
+++ b/src/ai/api/ai_list.hpp.sq
@@ -0,0 +1,28 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_list.hpp"
+
+namespace SQConvert {
+ /* Allow AIList to be used as Squirrel parameter */
+ template <> AIList *GetParam(ForceType<AIList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIList *)instance; }
+ template <> AIList &GetParam(ForceType<AIList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIList *)instance; }
+ template <> const AIList *GetParam(ForceType<const AIList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIList *)instance; }
+ template <> const AIList &GetParam(ForceType<const AIList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIList *)instance; }
+ template <> int Return<AIList *>(HSQUIRRELVM vm, AIList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIList", res, NULL, DefSQDestructorCallback<AIList>); return 1; }
+}; // namespace SQConvert
+
+void SQAIList_Register(Squirrel *engine) {
+ DefSQClass <AIList> SQAIList("AIList");
+ SQAIList.PreRegister(engine, "AIAbstractList");
+ SQAIList.AddConstructor<void (AIList::*)(), 1>(engine, "x");
+
+ SQAIList.DefSQStaticMethod(engine, &AIList::GetClassName, "GetClassName", 1, "x");
+
+ SQAIList.DefSQMethod(engine, &AIList::AddItem, "AddItem", 3, "xii");
+ SQAIList.DefSQMethod(engine, &AIList::ChangeItem, "ChangeItem", 3, "xii");
+ SQAIList.DefSQMethod(engine, &AIList::RemoveItem, "RemoveItem", 2, "xi");
+ SQAIList.DefSQAdvancedMethod(engine, &AIList::_set, "_set");
+
+ SQAIList.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_log.cpp b/src/ai/api/ai_log.cpp
new file mode 100644
index 000000000..6dc9cb365
--- /dev/null
+++ b/src/ai/api/ai_log.cpp
@@ -0,0 +1,84 @@
+/* $Id$ */
+
+/** @file ai_log.cpp Implementation of AILog. */
+
+#include "ai_log.hpp"
+#include "../../core/alloc_func.hpp"
+#include "../../company_func.h"
+#include "../../debug.h"
+#include "../../window_func.h"
+
+/* static */ void AILog::Info(const char *message)
+{
+ AILog::Log(LOG_INFO, message);
+}
+
+/* static */ void AILog::Warning(const char *message)
+{
+ AILog::Log(LOG_WARNING, message);
+}
+
+/* static */ void AILog::Error(const char *message)
+{
+ AILog::Log(LOG_ERROR, message);
+}
+
+/* static */ void AILog::Log(AILog::AILogType level, const char *message)
+{
+ if (AIObject::GetLogPointer() == NULL) {
+ AIObject::GetLogPointer() = new LogData();
+ LogData *log = (LogData *)AIObject::GetLogPointer();
+
+ log->lines = CallocT<char *>(80);
+ log->type = CallocT<AILog::AILogType>(80);
+ log->count = 80;
+ log->pos = log->count;
+ log->used = 0;
+ }
+ LogData *log = (LogData *)AIObject::GetLogPointer();
+
+ /* Go to the next log-line */
+ log->pos = (log->pos + 1) % log->count;
+
+ if (log->used != log->count) log->used++;
+
+ /* Free last message, and write new message */
+ free(log->lines[log->pos]);
+ log->lines[log->pos] = strdup(message);
+ log->type[log->pos] = level;
+
+ /* Cut string after first \n */
+ char *p;
+ while ((p = strchr(log->lines[log->pos], '\n')) != NULL) {
+ *p = '\0';
+ break;
+ }
+
+ char logc;
+
+ switch (level) {
+ case LOG_SQ_ERROR: logc = 'S'; break;
+ case LOG_ERROR: logc = 'E'; break;
+ case LOG_SQ_INFO: logc = 'P'; break;
+ case LOG_WARNING: logc = 'W'; break;
+ case LOG_INFO: logc = 'I'; break;
+ default: logc = '?'; break;
+ }
+
+ /* Also still print to debug window */
+ DEBUG(ai, level, "[%d] [%c] %s", (uint)_current_company, logc, log->lines[log->pos]);
+ InvalidateWindowData(WC_AI_DEBUG, 0, _current_company);
+}
+
+/* static */ void AILog::FreeLogPointer()
+{
+ LogData *log = (LogData *)AIObject::GetLogPointer();
+
+ for (int i = 0; i < log->count; i++) {
+ free(log->lines[i]);
+ }
+
+ free(log->lines);
+ free(log->type);
+ delete log;
+}
diff --git a/src/ai/api/ai_log.hpp b/src/ai/api/ai_log.hpp
new file mode 100644
index 000000000..7fd1cdb64
--- /dev/null
+++ b/src/ai/api/ai_log.hpp
@@ -0,0 +1,76 @@
+/* $Id$ */
+
+/** @file ai_log.hpp Everything to handle and issue log messages. */
+
+#ifndef AI_LOG_HPP
+#define AI_LOG_HPP
+
+#include "ai_object.hpp"
+
+/**
+ * Class that handles all log related functions.
+ */
+class AILog : public AIObject {
+ /* AIController needs access to Enum and Log, in order to keep the flow from
+ * OpenTTD core to NoAI API clear and simple. */
+ friend class AIController;
+
+public:
+ static const char *GetClassName() { return "AILog"; }
+
+ /**
+ * Log levels; The value is also feed to DEBUG() lvl.
+ * This has no use for you, as AI writer.
+ */
+ enum AILogType {
+ LOG_SQ_ERROR = 0, //!< Squirrel printed an error.
+ LOG_ERROR = 1, //!< User printed an error.
+ LOG_SQ_INFO = 2, //!< Squirrel printed some info.
+ LOG_WARNING = 3, //!< User printed some warning.
+ LOG_INFO = 4, //!< User printed some info.
+ };
+
+ /**
+ * Internal representation of the log-data inside the AI.
+ * This has no use for you, as AI writer.
+ */
+ struct LogData {
+ char **lines; //!< The log-lines.
+ AILog::AILogType *type; //!< Per line, which type of log it was.
+ int count; //!< Total amount of log-lines possible.
+ int pos; //!< Current position in lines.
+ int used; //!< Total amount of used log-lines.
+ };
+
+ /**
+ * Print an Info message to the logs.
+ * @param message The message to log.
+ */
+ static void Info(const char *message);
+
+ /**
+ * Print a Warning message to the logs.
+ * @param message The message to log.
+ */
+ static void Warning(const char *message);
+
+ /**
+ * Print an Error message to the logs.
+ * @param message The message to log.
+ */
+ static void Error(const char *message);
+
+ /**
+ * Free the log pointer.
+ * @note DO NOT CALL YOURSELF; leave it to the internal AI programming.
+ */
+ static void FreeLogPointer();
+
+private:
+ /**
+ * Internal command to log the message in a common way.
+ */
+ static void Log(AILog::AILogType level, const char *message);
+};
+
+#endif /* AI_LOG_HPP */
diff --git a/src/ai/api/ai_log.hpp.sq b/src/ai/api/ai_log.hpp.sq
new file mode 100644
index 000000000..7b84dc8f1
--- /dev/null
+++ b/src/ai/api/ai_log.hpp.sq
@@ -0,0 +1,37 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_log.hpp"
+
+namespace SQConvert {
+ /* Allow enums to be used as Squirrel parameters */
+ template <> AILog::AILogType GetParam(ForceType<AILog::AILogType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AILog::AILogType)tmp; }
+ template <> int Return<AILog::AILogType>(HSQUIRRELVM vm, AILog::AILogType res) { sq_pushinteger(vm, (int32)res); return 1; }
+
+ /* Allow AILog to be used as Squirrel parameter */
+ template <> AILog *GetParam(ForceType<AILog *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AILog *)instance; }
+ template <> AILog &GetParam(ForceType<AILog &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AILog *)instance; }
+ template <> const AILog *GetParam(ForceType<const AILog *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AILog *)instance; }
+ template <> const AILog &GetParam(ForceType<const AILog &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AILog *)instance; }
+ template <> int Return<AILog *>(HSQUIRRELVM vm, AILog *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AILog", res, NULL, DefSQDestructorCallback<AILog>); return 1; }
+}; // namespace SQConvert
+
+void SQAILog_Register(Squirrel *engine) {
+ DefSQClass <AILog> SQAILog("AILog");
+ SQAILog.PreRegister(engine);
+ SQAILog.AddConstructor<void (AILog::*)(), 1>(engine, "x");
+
+ SQAILog.DefSQConst(engine, AILog::LOG_SQ_ERROR, "LOG_SQ_ERROR");
+ SQAILog.DefSQConst(engine, AILog::LOG_ERROR, "LOG_ERROR");
+ SQAILog.DefSQConst(engine, AILog::LOG_SQ_INFO, "LOG_SQ_INFO");
+ SQAILog.DefSQConst(engine, AILog::LOG_WARNING, "LOG_WARNING");
+ SQAILog.DefSQConst(engine, AILog::LOG_INFO, "LOG_INFO");
+
+ SQAILog.DefSQStaticMethod(engine, &AILog::GetClassName, "GetClassName", 1, "x");
+ SQAILog.DefSQStaticMethod(engine, &AILog::Info, "Info", 2, "xs");
+ SQAILog.DefSQStaticMethod(engine, &AILog::Warning, "Warning", 2, "xs");
+ SQAILog.DefSQStaticMethod(engine, &AILog::Error, "Error", 2, "xs");
+ SQAILog.DefSQStaticMethod(engine, &AILog::FreeLogPointer, "FreeLogPointer", 1, "x");
+
+ SQAILog.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_map.cpp b/src/ai/api/ai_map.cpp
new file mode 100644
index 000000000..2c8547f95
--- /dev/null
+++ b/src/ai/api/ai_map.cpp
@@ -0,0 +1,62 @@
+/* $Id$ */
+
+/** @file ai_map.cpp Implementation of AIMap. */
+
+#include "ai_map.hpp"
+#include "../../map_func.h"
+#include "../../tile_map.h"
+
+/* static */ bool AIMap::IsValidTile(TileIndex t)
+{
+ return ::IsValidTile(t);
+}
+
+/* static */ TileIndex AIMap::GetMapSize()
+{
+ return ::MapSize();
+}
+
+/* static */ uint32 AIMap::GetMapSizeX()
+{
+ return ::MapSizeX();
+}
+
+/* static */ uint32 AIMap::GetMapSizeY()
+{
+ return ::MapSizeY();
+}
+
+/* static */ uint32 AIMap::GetTileX(TileIndex t)
+{
+ return ::TileX(t);
+}
+
+/* static */ uint32 AIMap::GetTileY(TileIndex t)
+{
+ return ::TileY(t);
+}
+
+/* static */ TileIndex AIMap::GetTileIndex(uint32 x, uint32 y)
+{
+ return ::TileXY(x, y);
+}
+
+/* static */ uint32 AIMap::DistanceManhattan(TileIndex t1, TileIndex t2)
+{
+ return ::DistanceManhattan(t1, t2);
+}
+
+/* static */ uint32 AIMap::DistanceMax(TileIndex t1, TileIndex t2)
+{
+ return ::DistanceMax(t1, t2);
+}
+
+/* static */ uint32 AIMap::DistanceSquare(TileIndex t1, TileIndex t2)
+{
+ return ::DistanceSquare(t1, t2);
+}
+
+/* static */ uint32 AIMap::DistanceFromEdge(TileIndex t)
+{
+ return ::DistanceFromEdge(t);
+}
diff --git a/src/ai/api/ai_map.hpp b/src/ai/api/ai_map.hpp
new file mode 100644
index 000000000..f14c360eb
--- /dev/null
+++ b/src/ai/api/ai_map.hpp
@@ -0,0 +1,117 @@
+/* $Id$ */
+
+/** @file ai_map.hpp Everything to query and manipulate map metadata. */
+
+#ifndef AI_MAP_HPP
+#define AI_MAP_HPP
+
+#include "ai_object.hpp"
+
+/**
+ * Class that handles all map related functions.
+ */
+class AIMap : public AIObject {
+public:
+ static const char *GetClassName() { return "AIMap"; }
+
+ /**
+ * Checks whether the given tile is valid.
+ * @param tile The tile to check.
+ * @return True is the tile it within the boundaries of the map.
+ */
+ static bool IsValidTile(TileIndex tile);
+
+ /**
+ * Gets the number of tiles in the map.
+ * @return The size of the map in tiles.
+ * @post Return value is always positive.
+ */
+ static TileIndex GetMapSize();
+
+ /**
+ * Gets the amount of tiles along the SW and NE border.
+ * @return The length along the SW and NE borders.
+ * @post Return value is always positive.
+ */
+ static uint32 GetMapSizeX();
+
+ /**
+ * Gets the amount of tiles along the SE and NW border.
+ * @return The length along the SE and NW borders.
+ * @post Return value is always positive.
+ */
+ static uint32 GetMapSizeY();
+
+ /**
+ * Gets the place along the SW/NE border (X-value).
+ * @param tile The tile to get the X-value of.
+ * @pre IsValidTile(tile).
+ * @return The X-value.
+ * @post Return value is always lower than GetMapSizeX().
+ */
+ static uint32 GetTileX(TileIndex tile);
+
+ /**
+ * Gets the place along the SE/NW border (Y-value).
+ * @param tile The tile to get the Y-value of.
+ * @pre IsValidTile(tile).
+ * @return The Y-value.
+ * @post Return value is always lower than GetMapSizeY().
+ */
+ static uint32 GetTileY(TileIndex tile);
+
+ /**
+ * Gets the TileIndex given a x,y-coordinate.
+ * @param x The X coordinate.
+ * @param y The Y coordinate.
+ * @pre x < GetMapSizeX().
+ * @pre y < GetMapSizeY().
+ * @return The TileIndex for the given (x,y) coordinate.
+ */
+ static TileIndex GetTileIndex(uint32 x, uint32 y);
+
+ /**
+ * Calculates the Manhattan distance; the difference of
+ * the X and Y added together.
+ * @param tile_from The start tile.
+ * @param tile_to The destination tile.
+ * @pre IsValidTile(tile_from).
+ * @pre IsValidTile(tile_to).
+ * @return The Manhattan distance between the tiles.
+ */
+ static uint32 DistanceManhattan(TileIndex tile_from, TileIndex tile_to);
+
+ /**
+ * Calculates the distance between two tiles via 1D calculation.
+ * This means the distance between X or the distance between Y, depending
+ * on which one is bigger.
+ * @param tile_from The start tile.
+ * @param tile_to The destination tile.
+ * @pre IsValidTile(tile_from).
+ * @pre IsValidTile(tile_to).
+ * @return The maximum distance between the tiles.
+ */
+ static uint32 DistanceMax(TileIndex tile_from, TileIndex tile_to);
+
+ /**
+ * The squared distance between the two tiles.
+ * This is the distance is the length of the shortest straight line
+ * between both points.
+ * @param tile_from The start tile.
+ * @param tile_to The destination tile.
+ * @pre IsValidTile(tile_from).
+ * @pre IsValidTile(tile_to).
+ * @return The squared distance between the tiles.
+ */
+ static uint32 DistanceSquare(TileIndex tile_from, TileIndex tile_to);
+
+ /**
+ * Calculates the shortest distance to the edge.
+ * @param tile From where the distance has to be calculated.
+ * @pre IsValidTile(tile).
+ * @return The distances to the closest edge.
+ */
+ static uint32 DistanceFromEdge(TileIndex tile);
+};
+
+#endif /* AI_MAP_HPP */
diff --git a/src/ai/api/ai_map.hpp.sq b/src/ai/api/ai_map.hpp.sq
new file mode 100644
index 000000000..f65368c8f
--- /dev/null
+++ b/src/ai/api/ai_map.hpp.sq
@@ -0,0 +1,34 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_map.hpp"
+
+namespace SQConvert {
+ /* Allow AIMap to be used as Squirrel parameter */
+ template <> AIMap *GetParam(ForceType<AIMap *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIMap *)instance; }
+ template <> AIMap &GetParam(ForceType<AIMap &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIMap *)instance; }
+ template <> const AIMap *GetParam(ForceType<const AIMap *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIMap *)instance; }
+ template <> const AIMap &GetParam(ForceType<const AIMap &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIMap *)instance; }
+ template <> int Return<AIMap *>(HSQUIRRELVM vm, AIMap *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIMap", res, NULL, DefSQDestructorCallback<AIMap>); return 1; }
+}; // namespace SQConvert
+
+void SQAIMap_Register(Squirrel *engine) {
+ DefSQClass <AIMap> SQAIMap("AIMap");
+ SQAIMap.PreRegister(engine);
+ SQAIMap.AddConstructor<void (AIMap::*)(), 1>(engine, "x");
+
+ SQAIMap.DefSQStaticMethod(engine, &AIMap::GetClassName, "GetClassName", 1, "x");
+ SQAIMap.DefSQStaticMethod(engine, &AIMap::IsValidTile, "IsValidTile", 2, "xi");
+ SQAIMap.DefSQStaticMethod(engine, &AIMap::GetMapSize, "GetMapSize", 1, "x");
+ SQAIMap.DefSQStaticMethod(engine, &AIMap::GetMapSizeX, "GetMapSizeX", 1, "x");
+ SQAIMap.DefSQStaticMethod(engine, &AIMap::GetMapSizeY, "GetMapSizeY", 1, "x");
+ SQAIMap.DefSQStaticMethod(engine, &AIMap::GetTileX, "GetTileX", 2, "xi");
+ SQAIMap.DefSQStaticMethod(engine, &AIMap::GetTileY, "GetTileY", 2, "xi");
+ SQAIMap.DefSQStaticMethod(engine, &AIMap::GetTileIndex, "GetTileIndex", 3, "xii");
+ SQAIMap.DefSQStaticMethod(engine, &AIMap::DistanceManhattan, "DistanceManhattan", 3, "xii");
+ SQAIMap.DefSQStaticMethod(engine, &AIMap::DistanceMax, "DistanceMax", 3, "xii");
+ SQAIMap.DefSQStaticMethod(engine, &AIMap::DistanceSquare, "DistanceSquare", 3, "xii");
+ SQAIMap.DefSQStaticMethod(engine, &AIMap::DistanceFromEdge, "DistanceFromEdge", 2, "xi");
+
+ SQAIMap.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_marine.cpp b/src/ai/api/ai_marine.cpp
new file mode 100644
index 000000000..274ab5f6b
--- /dev/null
+++ b/src/ai/api/ai_marine.cpp
@@ -0,0 +1,143 @@
+/* $Id$ */
+
+/** @file ai_marine.cpp Implementation of AIMarine. */
+
+#include "ai_marine.hpp"
+#include "../../openttd.h"
+#include "../../command_type.h"
+#include "../../variables.h"
+#include "../../station_map.h"
+#include "../../water_map.h"
+#include "../../tile_cmd.h"
+
+
+/* static */ bool AIMarine::IsWaterDepotTile(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return false;
+
+ return ::IsTileType(tile, MP_WATER) && ::GetWaterTileType(tile) == WATER_TILE_DEPOT;
+}
+
+/* static */ bool AIMarine::IsDockTile(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return false;
+
+ return ::IsTileType(tile, MP_STATION) && ::IsDock(tile);
+}
+
+/* static */ bool AIMarine::IsBuoyTile(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return false;
+
+ return ::IsTileType(tile, MP_STATION) && ::IsBuoy(tile);
+}
+
+/* static */ bool AIMarine::IsLockTile(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return false;
+
+ return ::IsTileType(tile, MP_WATER) && ::GetWaterTileType(tile) == WATER_TILE_LOCK;
+}
+
+/* static */ bool AIMarine::IsCanalTile(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return false;
+
+ return ::IsTileType(tile, MP_WATER) && ::IsCanal(tile);
+}
+
+/* static */ bool AIMarine::AreWaterTilesConnected(TileIndex t1, TileIndex t2)
+{
+ if (!::IsValidTile(t1)) return false;
+ if (!::IsValidTile(t2)) return false;
+
+ /* Tiles not neighbouring */
+ if (::DistanceManhattan(t1, t2) != 1) return false;
+ if (t1 > t2) Swap(t1, t2);
+
+ uint32 gtts1 = ::GetTileTrackStatus(t1, TRANSPORT_WATER, 0);
+ uint32 gtts2 = ::GetTileTrackStatus(t2, TRANSPORT_WATER, 0);
+
+ /* Ship can't travel on one of the tiles. */
+ if (gtts1 == 0 || gtts2 == 0) return false;
+
+ DiagDirection to_other_tile = (TileX(t1) == TileX(t2)) ? DIAGDIR_SE : DIAGDIR_SW;
+
+ /* Check whether we can 'leave' the tile at the border and 'enter' the other tile at the border */
+ return (gtts1 & DiagdirReachesTrackdirs(ReverseDiagDir(to_other_tile))) != 0 && (gtts2 & DiagdirReachesTrackdirs(to_other_tile)) != 0;
+}
+
+/* static */ bool AIMarine::BuildWaterDepot(TileIndex tile, bool vertical)
+{
+ EnforcePrecondition(false, ::IsValidTile(tile));
+
+ return AIObject::DoCommand(tile, vertical, 0, CMD_BUILD_SHIP_DEPOT);
+}
+
+/* static */ bool AIMarine::BuildDock(TileIndex tile, bool join_adjacent)
+{
+ EnforcePrecondition(false, ::IsValidTile(tile));
+
+ return AIObject::DoCommand(tile, join_adjacent ? 0 : 1, INVALID_STATION << 16, CMD_BUILD_DOCK);
+}
+
+/* static */ bool AIMarine::BuildBuoy(TileIndex tile)
+{
+ EnforcePrecondition(false, ::IsValidTile(tile));
+
+ return AIObject::DoCommand(tile, 0, 0, CMD_BUILD_BUOY);
+}
+
+/* static */ bool AIMarine::BuildLock(TileIndex tile)
+{
+ EnforcePrecondition(false, ::IsValidTile(tile));
+
+ return AIObject::DoCommand(tile, 0, 0, CMD_BUILD_LOCK);
+}
+
+/* static */ bool AIMarine::BuildCanal(TileIndex tile)
+{
+ EnforcePrecondition(false, ::IsValidTile(tile));
+
+ return AIObject::DoCommand(tile, tile, 0, CMD_BUILD_CANAL);
+}
+
+/* static */ bool AIMarine::RemoveWaterDepot(TileIndex tile)
+{
+ EnforcePrecondition(false, ::IsValidTile(tile));
+ EnforcePrecondition(false, IsWaterDepotTile(tile));
+
+ return AIObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR);
+}
+
+/* static */ bool AIMarine::RemoveDock(TileIndex tile)
+{
+ EnforcePrecondition(false, ::IsValidTile(tile));
+ EnforcePrecondition(false, IsDockTile(tile));
+
+ return AIObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR);
+}
+
+/* static */ bool AIMarine::RemoveBuoy(TileIndex tile)
+{
+ EnforcePrecondition(false, ::IsValidTile(tile));
+ EnforcePrecondition(false, IsBuoyTile(tile));
+
+ return AIObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR);
+}
+
+/* static */ bool AIMarine::RemoveLock(TileIndex tile)
+{
+ EnforcePrecondition(false, ::IsValidTile(tile));
+ EnforcePrecondition(false, IsLockTile(tile));
+
+ return AIObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR);
+}
+
+/* static */ bool AIMarine::RemoveCanal(TileIndex tile)
+{
+ EnforcePrecondition(false, ::IsValidTile(tile));
+ EnforcePrecondition(false, IsCanalTile(tile));
+
+ return AIObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR);
+}
diff --git a/src/ai/api/ai_marine.hpp b/src/ai/api/ai_marine.hpp
new file mode 100644
index 000000000..9f871887d
--- /dev/null
+++ b/src/ai/api/ai_marine.hpp
@@ -0,0 +1,187 @@
+/* $Id$ */
+
+/** @file ai_marine.hpp Everything to query and build marine. */
+
+#ifndef AI_MARINE_HPP
+#define AI_MARINE_HPP
+
+#include "ai_object.hpp"
+#include "ai_error.hpp"
+
+/**
+ * Class that handles all marine related functions.
+ */
+class AIMarine : public AIObject {
+public:
+ static const char *GetClassName() { return "AIMarine"; }
+
+ /**
+ * All marine related error messages.
+ */
+ enum ErrorMessages {
+ /** Base for marine related errors */
+ ERR_MARINE_BASE = AIError::ERR_CAT_MARINE << AIError::ERR_CAT_BIT_SIZE,
+
+ /** Infrastructure must be built on water */
+ ERR_MARINE_MUST_BE_BUILT_ON_WATER, // [STR_3801_MUST_BE_BUILT_ON_WATER]
+ };
+
+ /**
+ * Checks whether the given tile is actually a tile with a water depot.
+ * @param tile The tile to check.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if and only if the tile has a water depot.
+ */
+ static bool IsWaterDepotTile(TileIndex tile);
+
+ /**
+ * Checks whether the given tile is actually a tile with a dock.
+ * @param tile The tile to check.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if and only if the tile has a dock.
+ */
+ static bool IsDockTile(TileIndex tile);
+
+ /**
+ * Checks whether the given tile is actually a tile with a buoy.
+ * @param tile The tile to check.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if and only if the tile has a buoy.
+ */
+ static bool IsBuoyTile(TileIndex tile);
+
+ /**
+ * Checks whether the given tile is actually a tile with a lock.
+ * @param tile The tile to check.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if and only if the tile has a lock.
+ */
+ static bool IsLockTile(TileIndex tile);
+
+ /**
+ * Checks whether the given tile is actually a tile with a canal.
+ * @param tile The tile to check.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if and only if the tile has a canal.
+ */
+ static bool IsCanalTile(TileIndex tile);
+
+ /**
+ * Checks whether the given tiles are directly connected, i.e. whether
+ * a ship vehicle can travel from the center of the first tile to the
+ * center of the second tile.
+ * @param tile_from The source tile.
+ * @param tile_to The destination tile.
+ * @pre AIMap::IsValidTile(tile_from).
+ * @pre AIMap::IsValidTile(tile_to).
+ * @pre 'tile_from' and 'tile_to' are directly neighbouring tiles.
+ * @return True if and only if a ship can go from tile_from to tile_to.
+ */
+ static bool AreWaterTilesConnected(TileIndex tile_from, TileIndex tile_to);
+
+ /**
+ * Builds a water depot on tile.
+ * @param tile The tile where the water depot will be build.
+ * @param vertical If true, depot will be vertical, else horizontal.
+ * @pre AIMap::IsValidTile(tile).
+ * @exception AIError::ERR_AREA_NOT_CLEAR
+ * @exception AIError::ERR_SITE_UNSUITABLE
+ * @exception AIMarine::ERR_MARINE_MUST_BE_BUILT_ON_WATER
+ * @return Whether the water depot has been/can be build or not.
+ * @note A WaterDepot is 1 tile in width, and 2 tiles in length.
+ */
+ static bool BuildWaterDepot(TileIndex tile, bool vertical);
+
+ /**
+ * Builds a dock where tile is the tile still on land.
+ * @param tile The tile still on land of the dock.
+ * @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).
+ * @exception AIError::ERR_AREA_NOT_CLEAR
+ * @exception AIError::ERR_SITE_UNSUITABLE
+ * @exception AIStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION
+ * @exception AIStation::ERR_STATION_TOO_MANY_STATIONS
+ * @return Whether the dock has been/can be build or not.
+ */
+ static bool BuildDock(TileIndex tile, bool join_adjacent);
+
+ /**
+ * Builds a buoy on tile.
+ * @param tile The tile where the buoy will be build.
+ * @pre AIMap::IsValidTile(tile).
+ * @exception AIError::ERR_AREA_NOT_CLEAR
+ * @exception AIError::ERR_SITE_UNSUITABLE
+ * @exception AIStation::ERR_STATION_TOO_MANY_STATIONS
+ * @return Whether the buoy has been/can be build or not.
+ */
+ static bool BuildBuoy(TileIndex tile);
+
+ /**
+ * Builds a lock on tile.
+ * @param tile The tile where the lock will be build.
+ * @pre AIMap::IsValidTile(tile).
+ * @exception AIError::ERR_LAND_SLOPED_WRONG
+ * @exception AIError::ERR_SITE_UNSUITABLE
+ * @return Whether the lock has been/can be build or not.
+ */
+ static bool BuildLock(TileIndex tile);
+
+ /**
+ * Builds a canal on tile.
+ * @param tile The tile where the canal will be build.
+ * @pre AIMap::IsValidTile(tile).
+ * @exception AIError::ERR_AREA_NOT_CLEAR
+ * @exception AIError::ERR_LAND_SLOPED_WRONG
+ * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
+ * @exception AIError::ERR_ALREADY_BUILT
+ * @return Whether the canal has been/can be build or not.
+ */
+ static bool BuildCanal(TileIndex tile);
+
+ /**
+ * Removes a water depot.
+ * @param tile Any tile of the water depot.
+ * @pre AIMap::IsValidTile(tile).
+ * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
+ * @return Whether the water depot has been/can be removed or not.
+ */
+ static bool RemoveWaterDepot(TileIndex tile);
+
+ /**
+ * Removes a dock.
+ * @param tile Any tile of the dock.
+ * @pre AIMap::IsValidTile(tile).
+ * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
+ * @return Whether the dock has been/can be removed or not.
+ */
+ static bool RemoveDock(TileIndex tile);
+
+ /**
+ * Removes a buoy.
+ * @param tile Any tile of the buoy.
+ * @pre AIMap::IsValidTile(tile).
+ * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
+ * @return Whether the buoy has been/can be removed or not.
+ */
+ static bool RemoveBuoy(TileIndex tile);
+
+ /**
+ * Removes a lock.
+ * @param tile Any tile of the lock.
+ * @pre AIMap::IsValidTile(tile).
+ * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
+ * @return Whether the lock has been/can be removed or not.
+ */
+ static bool RemoveLock(TileIndex tile);
+
+ /**
+ * Removes a canal.
+ * @param tile Any tile of the canal.
+ * @pre AIMap::IsValidTile(tile).
+ * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
+ * @return Whether the canal has been/can be removed or not.
+ */
+ static bool RemoveCanal(TileIndex tile);
+};
+
+#endif /* AI_MARINE_HPP */
diff --git a/src/ai/api/ai_marine.hpp.sq b/src/ai/api/ai_marine.hpp.sq
new file mode 100644
index 000000000..269c99604
--- /dev/null
+++ b/src/ai/api/ai_marine.hpp.sq
@@ -0,0 +1,50 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_marine.hpp"
+
+namespace SQConvert {
+ /* Allow enums to be used as Squirrel parameters */
+ template <> AIMarine::ErrorMessages GetParam(ForceType<AIMarine::ErrorMessages>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIMarine::ErrorMessages)tmp; }
+ template <> int Return<AIMarine::ErrorMessages>(HSQUIRRELVM vm, AIMarine::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; }
+
+ /* Allow AIMarine to be used as Squirrel parameter */
+ template <> AIMarine *GetParam(ForceType<AIMarine *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIMarine *)instance; }
+ template <> AIMarine &GetParam(ForceType<AIMarine &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIMarine *)instance; }
+ template <> const AIMarine *GetParam(ForceType<const AIMarine *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIMarine *)instance; }
+ template <> const AIMarine &GetParam(ForceType<const AIMarine &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIMarine *)instance; }
+ template <> int Return<AIMarine *>(HSQUIRRELVM vm, AIMarine *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIMarine", res, NULL, DefSQDestructorCallback<AIMarine>); return 1; }
+}; // namespace SQConvert
+
+void SQAIMarine_Register(Squirrel *engine) {
+ DefSQClass <AIMarine> SQAIMarine("AIMarine");
+ SQAIMarine.PreRegister(engine);
+ SQAIMarine.AddConstructor<void (AIMarine::*)(), 1>(engine, "x");
+
+ SQAIMarine.DefSQConst(engine, AIMarine::ERR_MARINE_BASE, "ERR_MARINE_BASE");
+ SQAIMarine.DefSQConst(engine, AIMarine::ERR_MARINE_MUST_BE_BUILT_ON_WATER, "ERR_MARINE_MUST_BE_BUILT_ON_WATER");
+
+ AIError::RegisterErrorMap(STR_3801_MUST_BE_BUILT_ON_WATER, AIMarine::ERR_MARINE_MUST_BE_BUILT_ON_WATER);
+
+ AIError::RegisterErrorMapString(AIMarine::ERR_MARINE_MUST_BE_BUILT_ON_WATER, "ERR_MARINE_MUST_BE_BUILT_ON_WATER");
+
+ SQAIMarine.DefSQStaticMethod(engine, &AIMarine::GetClassName, "GetClassName", 1, "x");
+ SQAIMarine.DefSQStaticMethod(engine, &AIMarine::IsWaterDepotTile, "IsWaterDepotTile", 2, "xi");
+ SQAIMarine.DefSQStaticMethod(engine, &AIMarine::IsDockTile, "IsDockTile", 2, "xi");
+ SQAIMarine.DefSQStaticMethod(engine, &AIMarine::IsBuoyTile, "IsBuoyTile", 2, "xi");
+ SQAIMarine.DefSQStaticMethod(engine, &AIMarine::IsLockTile, "IsLockTile", 2, "xi");
+ SQAIMarine.DefSQStaticMethod(engine, &AIMarine::IsCanalTile, "IsCanalTile", 2, "xi");
+ SQAIMarine.DefSQStaticMethod(engine, &AIMarine::AreWaterTilesConnected, "AreWaterTilesConnected", 3, "xii");
+ SQAIMarine.DefSQStaticMethod(engine, &AIMarine::BuildWaterDepot, "BuildWaterDepot", 3, "xib");
+ SQAIMarine.DefSQStaticMethod(engine, &AIMarine::BuildDock, "BuildDock", 3, "xib");
+ SQAIMarine.DefSQStaticMethod(engine, &AIMarine::BuildBuoy, "BuildBuoy", 2, "xi");
+ SQAIMarine.DefSQStaticMethod(engine, &AIMarine::BuildLock, "BuildLock", 2, "xi");
+ SQAIMarine.DefSQStaticMethod(engine, &AIMarine::BuildCanal, "BuildCanal", 2, "xi");
+ SQAIMarine.DefSQStaticMethod(engine, &AIMarine::RemoveWaterDepot, "RemoveWaterDepot", 2, "xi");
+ SQAIMarine.DefSQStaticMethod(engine, &AIMarine::RemoveDock, "RemoveDock", 2, "xi");
+ SQAIMarine.DefSQStaticMethod(engine, &AIMarine::RemoveBuoy, "RemoveBuoy", 2, "xi");
+ SQAIMarine.DefSQStaticMethod(engine, &AIMarine::RemoveLock, "RemoveLock", 2, "xi");
+ SQAIMarine.DefSQStaticMethod(engine, &AIMarine::RemoveCanal, "RemoveCanal", 2, "xi");
+
+ SQAIMarine.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_object.cpp b/src/ai/api/ai_object.cpp
new file mode 100644
index 000000000..664c24ecc
--- /dev/null
+++ b/src/ai/api/ai_object.cpp
@@ -0,0 +1,264 @@
+/* $Id$ */
+
+/** @file ai_object.cpp Implementation of AIObject. */
+
+#include "ai_object.hpp"
+#include "ai_log.hpp"
+#include "ai_error.hpp"
+#include "table/strings.h"
+#include "../../openttd.h"
+#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 "../ai.hpp"
+#include "ai_controller.hpp"
+#include "../ai_storage.hpp"
+#include "../ai_instance.hpp"
+
+#include <vector>
+
+static AIStorage *GetStorage()
+{
+ return AIInstance::GetStorage();
+}
+
+void AIObject::SetDoCommandDelay(uint ticks)
+{
+ assert(ticks > 0);
+ GetStorage()->delay = ticks;
+}
+
+uint AIObject::GetDoCommandDelay()
+{
+ return GetStorage()->delay;
+}
+
+void AIObject::SetDoCommandMode(AIModeProc *proc, AIObject *instance)
+{
+ GetStorage()->mode = proc;
+ GetStorage()->mode_instance = instance;
+}
+
+AIModeProc *AIObject::GetDoCommandMode()
+{
+ return GetStorage()->mode;
+}
+
+AIObject *AIObject::GetDoCommandModeInstance()
+{
+ return GetStorage()->mode_instance;
+}
+
+void AIObject::SetDoCommandCosts(Money value)
+{
+ GetStorage()->costs = CommandCost(value);
+}
+
+void AIObject::IncreaseDoCommandCosts(Money value)
+{
+ GetStorage()->costs.AddCost(value);
+}
+
+Money AIObject::GetDoCommandCosts()
+{
+ return GetStorage()->costs.GetCost();
+}
+
+void AIObject::SetLastError(AIErrorType last_error)
+{
+ GetStorage()->last_error = last_error;
+}
+
+AIErrorType AIObject::GetLastError()
+{
+ return GetStorage()->last_error;
+}
+
+void AIObject::SetLastCost(Money last_cost)
+{
+ GetStorage()->last_cost = last_cost;
+}
+
+Money AIObject::GetLastCost()
+{
+ return GetStorage()->last_cost;
+}
+
+void AIObject::SetRoadType(RoadType road_type)
+{
+ GetStorage()->road_type = road_type;
+}
+
+RoadType AIObject::GetRoadType()
+{
+ return GetStorage()->road_type;
+}
+
+void AIObject::SetRailType(RailType rail_type)
+{
+ GetStorage()->rail_type = rail_type;
+}
+
+RailType AIObject::GetRailType()
+{
+ return GetStorage()->rail_type;
+}
+
+void AIObject::SetLastCommandRes(bool res)
+{
+ GetStorage()->last_command_res = res;
+ /* Also store the results of various global variables */
+ SetNewVehicleID(_new_vehicle_id);
+ SetNewSignID(_new_sign_id);
+ SetNewTunnelEndtile(_build_tunnel_endtile);
+ SetNewGroupID(_new_group_id);
+}
+
+bool AIObject::GetLastCommandRes()
+{
+ return GetStorage()->last_command_res;
+}
+
+void AIObject::SetNewVehicleID(VehicleID vehicle_id)
+{
+ GetStorage()->new_vehicle_id = vehicle_id;
+}
+
+VehicleID AIObject::GetNewVehicleID()
+{
+ return GetStorage()->new_vehicle_id;
+}
+
+void AIObject::SetNewSignID(SignID sign_id)
+{
+ GetStorage()->new_sign_id = sign_id;
+}
+
+SignID AIObject::GetNewSignID()
+{
+ return GetStorage()->new_sign_id;
+}
+
+void AIObject::SetNewTunnelEndtile(TileIndex tile)
+{
+ GetStorage()->new_tunnel_endtile = tile;
+}
+
+TileIndex AIObject::GetNewTunnelEndtile()
+{
+ return GetStorage()->new_tunnel_endtile;
+}
+
+void AIObject::SetNewGroupID(GroupID group_id)
+{
+ GetStorage()->new_group_id = group_id;
+}
+
+GroupID AIObject::GetNewGroupID()
+{
+ return GetStorage()->new_group_id;
+}
+
+void AIObject::SetAllowDoCommand(bool allow)
+{
+ GetStorage()->allow_do_command = allow;
+}
+
+bool AIObject::GetAllowDoCommand()
+{
+ return GetStorage()->allow_do_command;
+}
+
+void *&AIObject::GetEventPointer()
+{
+ return GetStorage()->event_data;
+}
+
+void *&AIObject::GetLogPointer()
+{
+ return GetStorage()->log_data;
+}
+
+void AIObject::SetCallbackVariable(int index, int value)
+{
+ if ((size_t)index >= GetStorage()->callback_value.size()) GetStorage()->callback_value.resize(index + 1);
+ GetStorage()->callback_value[index] = value;
+}
+
+int AIObject::GetCallbackVariable(int index)
+{
+ return GetStorage()->callback_value[index];
+}
+
+bool AIObject::DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint cmd, const char *text, AISuspendCallbackProc *callback)
+{
+ if (AIObject::GetAllowDoCommand() == false) {
+ AILog::Error("You are not allowed to execute any DoCommand (even indirect) in your constructor, Save(), and Load().\n");
+ return false;
+ }
+
+ CompanyID old_company;
+ CommandCost res;
+
+ /* Set the default callback to return a true/false result of the DoCommand */
+ if (callback == NULL) callback = &AIInstance::DoCommandReturn;
+
+ /* Make sure the last error is reset, so we don't give faulty warnings */
+ SetLastError(AIError::ERR_NONE);
+
+ /* First, do a test-run to see if we can do this */
+ res = ::DoCommand(tile, p1, p2, CommandFlagsToDCFlags(GetCommandFlags(cmd)), cmd, text);
+ /* The command failed, so return */
+ if (::CmdFailed(res)) {
+ SetLastError(AIError::StringToError(_error_message));
+ return false;
+ }
+
+ /* Check what the callback wants us to do */
+ if (GetDoCommandMode() != NULL && !GetDoCommandMode()(tile, p1, p2, cmd, res)) {
+ IncreaseDoCommandCosts(res.GetCost());
+ return true;
+ }
+
+#ifdef ENABLE_NETWORK
+ /* Send the command */
+ if (_networking) {
+ /* NetworkSend_Command needs _local_company to be set correctly, so
+ * adjust it, and put it back right after the function */
+ old_company = _local_company;
+ _local_company = _current_company;
+ ::NetworkSend_Command(tile, p1, p2, cmd, CcAI, text);
+ _local_company = old_company;
+ SetLastCost(res.GetCost());
+
+ /* Suspend the AI till the command is really executed */
+ throw AI_VMSuspend(-(int)GetDoCommandDelay(), callback);
+ } else {
+#else
+ {
+#endif
+ /* For SinglePlayer we execute the command immediatly */
+ if (!::DoCommandP(tile, p1, p2, cmd, NULL, text)) res = CMD_ERROR;
+ SetLastCommandRes(!::CmdFailed(res));
+
+ if (::CmdFailed(res)) {
+ SetLastError(AIError::StringToError(_error_message));
+ return false;
+ }
+ SetLastCost(res.GetCost());
+ IncreaseDoCommandCosts(res.GetCost());
+
+ /* Suspend the AI player for 1+ ticks, so it simulates multiplayer. This
+ * both avoids confusion when a developer launched his AI in a
+ * multiplayer game, but also gives time for the GUI and human player
+ * to interact with the game. */
+ throw AI_VMSuspend(GetDoCommandDelay(), callback);
+ }
+
+ NOT_REACHED();
+}
diff --git a/src/ai/api/ai_object.hpp b/src/ai/api/ai_object.hpp
new file mode 100644
index 000000000..9dcb0f4cc
--- /dev/null
+++ b/src/ai/api/ai_object.hpp
@@ -0,0 +1,202 @@
+/* $Id$ */
+
+/** @file ai_object.hpp Main object, on which all objects depend. */
+
+#ifndef AI_OBJECT_HPP
+#define AI_OBJECT_HPP
+
+#include "../../stdafx.h"
+#include "../../misc/countedptr.hpp"
+#include "../../road_type.h"
+#include "../../rail_type.h"
+
+#include "ai_types.hpp"
+
+/**
+ * The callback function when an AI suspends.
+ */
+typedef void (AISuspendCallbackProc)(class AIInstance *instance);
+
+/**
+ * The callback function for Mode-classes.
+ */
+typedef bool (AIModeProc)(TileIndex tile, uint32 p1, uint32 p2, uint cmd, CommandCost costs);
+
+/**
+ * Uper-parent object of all API classes. You should never use this class in
+ * your AI, as it doesn't publish any public functions. It is used
+ * internally to have a common place to handle general things, like internal
+ * command processing, and command-validation checks.
+ */
+class AIObject : public SimpleCountedObject {
+friend void CcAI(bool success, TileIndex tile, uint32 p1, uint32 p2);
+friend class AIInstance;
+friend class AIController;
+protected:
+ /**
+ * Executes a raw DoCommand for the AI.
+ */
+ static bool DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint cmd, const char *text = NULL, AISuspendCallbackProc *callback = NULL);
+
+ /**
+ * Sets the DoCommand costs counter to a value.
+ */
+ static void SetDoCommandCosts(Money value);
+
+ /**
+ * Increase the current value of the DoCommand costs counter.
+ */
+ static void IncreaseDoCommandCosts(Money value);
+
+ /**
+ * Get the current DoCommand costs counter.
+ */
+ static Money GetDoCommandCosts();
+
+ /**
+ * Set the DoCommand last error.
+ */
+ static void SetLastError(AIErrorType last_error);
+
+ /**
+ * Get the DoCommand last error.
+ */
+ static AIErrorType GetLastError();
+
+ /**
+ * Set the road type.
+ */
+ static void SetRoadType(RoadType road_type);
+
+ /**
+ * Get the road type.
+ */
+ static RoadType GetRoadType();
+
+ /**
+ * Set the rail type.
+ */
+ static void SetRailType(RailType rail_type);
+
+ /**
+ * Get the rail type.
+ */
+ static RailType GetRailType();
+
+ /**
+ * Set the current mode of your AI to this proc.
+ */
+ static void SetDoCommandMode(AIModeProc *proc, AIObject *instance);
+
+ /**
+ * Get the current mode your AI is currently under.
+ */
+ static AIModeProc *GetDoCommandMode();
+
+ /**
+ * Get the instance of the current mode your AI is currently under.
+ */
+ static AIObject *GetDoCommandModeInstance();
+
+ /**
+ * Set the delay of the DoCommand.
+ */
+ static void SetDoCommandDelay(uint ticks);
+
+ /**
+ * Get the delay of the DoCommand.
+ */
+ static uint GetDoCommandDelay();
+
+ /**
+ * Get the latest result of a DoCommand.
+ */
+ static bool GetLastCommandRes();
+
+ /**
+ * Get the latest stored new_vehicle_id.
+ */
+ static VehicleID GetNewVehicleID();
+
+ /**
+ * Get the latest stored new_sign_id.
+ */
+ static SignID GetNewSignID();
+
+ /**
+ * Get the latest stored new_tunnel_endtile.
+ */
+ static TileIndex GetNewTunnelEndtile();
+
+ /**
+ * Get the latest stored new_group_id.
+ */
+ static GroupID GetNewGroupID();
+
+ /**
+ * Get the latest stored allow_do_command.
+ * If this is false, you are not allowed to do any DoCommands.
+ */
+ static bool GetAllowDoCommand();
+
+ /**
+ * Get the pointer to store event data in.
+ */
+ static void *&GetEventPointer();
+
+ static void SetLastCost(Money last_cost);
+ static Money GetLastCost();
+ static void SetCallbackVariable(int index, int value);
+ static int GetCallbackVariable(int index);
+
+public:
+ /**
+ * Store the latest result of a DoCommand per company.
+ * @note NEVER use this yourself in your AI!
+ * @param res The result of the last command.
+ */
+ static void SetLastCommandRes(bool res);
+
+ /**
+ * Store a new_vehicle_id per company.
+ * @note NEVER use this yourself in your AI!
+ * @param vehicle_id The new VehicleID.
+ */
+ static void SetNewVehicleID(VehicleID vehicle_id);
+
+ /**
+ * Store a new_sign_id per company.
+ * @note NEVER use this yourself in your AI!
+ * @param sign_id The new SignID.
+ */
+ static void SetNewSignID(SignID sign_id);
+
+ /**
+ * Store a new_tunnel_endtile per company.
+ * @note NEVER use this yourself in your AI!
+ * @param tile The new TileIndex.
+ */
+ static void SetNewTunnelEndtile(TileIndex tile);
+
+ /**
+ * Store a new_group_id per company.
+ * @note NEVER use this yourself in your AI!
+ * @param group_id The new GroupID.
+ */
+ static void SetNewGroupID(GroupID group_id);
+
+ /**
+ * Store a allow_do_command per company.
+ * @note NEVER use this yourself in your AI!
+ * @param allow The new allow.
+ */
+ static void SetAllowDoCommand(bool allow);
+
+ /**
+ * Get the pointer to store log message in.
+ * @note NEVER use this yourself in your AI!
+ */
+ static void *&GetLogPointer();
+};
+
+#endif /* AI_OBJECT_HPP */
diff --git a/src/ai/api/ai_order.cpp b/src/ai/api/ai_order.cpp
new file mode 100644
index 000000000..f48c5e392
--- /dev/null
+++ b/src/ai/api/ai_order.cpp
@@ -0,0 +1,296 @@
+/* $Id$ */
+
+/** @file ai_order.cpp Implementation of AIOrder. */
+
+#include "ai_order.hpp"
+#include "ai_map.hpp"
+#include "ai_vehicle.hpp"
+#include "../ai_instance.hpp"
+#include "../../openttd.h"
+#include "../../debug.h"
+#include "../../vehicle_base.h"
+#include "../../depot_base.h"
+#include "../../landscape.h"
+#include "../../rail_map.h"
+#include "../../road_map.h"
+#include "../../station_map.h"
+#include "../../water_map.h"
+#include "../../waypoint.h"
+
+/**
+ * Gets the order type given a tile
+ * @param t the tile to get the order from
+ * @return the order type, or OT_END when there is no order
+ */
+static OrderType GetOrderTypeByTile(TileIndex t)
+{
+ if (!::IsValidTile(t)) return OT_END;
+
+ switch (::GetTileType(t)) {
+ default: break;
+ case MP_STATION: return OT_GOTO_STATION; break;
+ case MP_WATER: if (::IsShipDepot(t)) return OT_GOTO_DEPOT; break;
+ case MP_ROAD: if (::GetRoadTileType(t) == ROAD_TILE_DEPOT) return OT_GOTO_DEPOT; break;
+ case MP_RAILWAY:
+ switch (::GetRailTileType(t)) {
+ case RAIL_TILE_DEPOT: return OT_GOTO_DEPOT;
+ case RAIL_TILE_WAYPOINT: return OT_GOTO_WAYPOINT;
+ default: break;
+ }
+ break;
+ }
+
+ return OT_END;
+}
+
+/* static */ bool AIOrder::IsValidVehicleOrder(VehicleID vehicle_id, OrderPosition order_position)
+{
+ return AIVehicle::IsValidVehicle(vehicle_id) && order_position >= 0 && (order_position < ::GetVehicle(vehicle_id)->GetNumOrders() || order_position == CURRENT_ORDER);
+}
+
+/* static */ AIOrder::OrderPosition AIOrder::ResolveOrderPosition(VehicleID vehicle_id, OrderPosition order_position)
+{
+ if (!AIVehicle::IsValidVehicle(vehicle_id)) return INVALID_ORDER;
+
+ if (order_position == CURRENT_ORDER) return (AIOrder::OrderPosition)::GetVehicle(vehicle_id)->cur_order_index;
+ return (order_position >= 0 && order_position < ::GetVehicle(vehicle_id)->GetNumOrders()) ? order_position : INVALID_ORDER;
+}
+
+
+/* static */ bool AIOrder::AreOrderFlagsValid(TileIndex destination, AIOrderFlags order_flags)
+{
+ switch (::GetOrderTypeByTile(destination)) {
+ case OT_GOTO_STATION:
+ return ((order_flags & ~(AIOF_NON_STOP_FLAGS | AIOF_UNLOAD_FLAGS | AIOF_LOAD_FLAGS)) == 0) &&
+ /* Test the different mutual exclusive flags. */
+ (((order_flags & AIOF_TRANSFER) == 0) || ((order_flags & AIOF_UNLOAD) == 0)) &&
+ (((order_flags & AIOF_TRANSFER) == 0) || ((order_flags & AIOF_NO_UNLOAD) == 0)) &&
+ (((order_flags & AIOF_UNLOAD) == 0) || ((order_flags & AIOF_NO_UNLOAD) == 0)) &&
+ (((order_flags & AIOF_UNLOAD) == 0) || ((order_flags & AIOF_NO_UNLOAD) == 0)) &&
+ (((order_flags & AIOF_NO_UNLOAD) == 0) || ((order_flags & AIOF_NO_LOAD) == 0)) &&
+ (((order_flags & AIOF_FULL_LOAD_ANY) == 0) || ((order_flags & AIOF_NO_LOAD) == 0));
+
+ case OT_GOTO_DEPOT: return (order_flags & ~(AIOF_NON_STOP_FLAGS | AIOF_SERVICE_IF_NEEDED)) == 0;
+ case OT_GOTO_WAYPOINT: return (order_flags & ~(AIOF_NON_STOP_FLAGS)) == 0;
+ default: return false;
+ }
+}
+
+/* static */ int32 AIOrder::GetOrderCount(VehicleID vehicle_id)
+{
+ return AIVehicle::IsValidVehicle(vehicle_id) ? ::GetVehicle(vehicle_id)->GetNumOrders() : -1;
+}
+
+/* static */ TileIndex AIOrder::GetOrderDestination(VehicleID vehicle_id, OrderPosition order_position)
+{
+ if (!IsValidVehicleOrder(vehicle_id, order_position)) return INVALID_TILE;
+
+ const Order *order;
+ const Vehicle *v = ::GetVehicle(vehicle_id);
+ if (order_position == CURRENT_ORDER) {
+ order = &v->current_order;
+ } else {
+ order = v->GetFirstOrder();
+ for (int i = 0; i < order_position; i++) order = order->next;
+ }
+
+ switch (order->GetType()) {
+ case OT_GOTO_DEPOT:
+ if (v->type != VEH_AIRCRAFT) return ::GetDepot(order->GetDestination())->xy;
+ /* FALL THROUGH: aircraft's hangars are referenced by StationID, not DepotID */
+
+ case OT_GOTO_STATION: return ::GetStation(order->GetDestination())->xy;
+ case OT_GOTO_WAYPOINT: return ::GetWaypoint(order->GetDestination())->xy;
+ default: return INVALID_TILE;
+ }
+}
+
+/* static */ AIOrder::AIOrderFlags AIOrder::GetOrderFlags(VehicleID vehicle_id, OrderPosition order_position)
+{
+ if (!IsValidVehicleOrder(vehicle_id, order_position)) return AIOF_INVALID;
+
+ const Order *order;
+ if (order_position == CURRENT_ORDER) {
+ order = &::GetVehicle(vehicle_id)->current_order;
+ } else {
+ order = ::GetVehicle(vehicle_id)->GetFirstOrder();
+ for (int i = 0; i < order_position; i++) order = order->next;
+ }
+
+ AIOrderFlags order_flags = AIOF_NONE;
+ order_flags |= (AIOrderFlags)order->GetNonStopType();
+ switch (order->GetType()) {
+ case OT_GOTO_DEPOT:
+ if (order->GetDepotOrderType() & ODTFB_SERVICE) order_flags |= AIOF_SERVICE_IF_NEEDED;
+ break;
+
+ case OT_GOTO_STATION:
+ order_flags |= (AIOrderFlags)(order->GetLoadType() << 5);
+ order_flags |= (AIOrderFlags)(order->GetUnloadType() << 2);
+ break;
+
+ default: break;
+ }
+
+ return order_flags;
+}
+
+/* static */ bool AIOrder::AppendOrder(VehicleID vehicle_id, TileIndex destination, AIOrderFlags order_flags)
+{
+ EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
+ return InsertOrder(vehicle_id, (AIOrder::OrderPosition)::GetVehicle(vehicle_id)->GetNumOrders(), destination, order_flags);
+}
+
+/* static */ bool AIOrder::InsertOrder(VehicleID vehicle_id, OrderPosition order_position, TileIndex destination, AIOrder::AIOrderFlags order_flags)
+{
+ /* IsValidVehicleOrder is not good enough because it does not allow appending. */
+ if (order_position == CURRENT_ORDER) order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position);
+
+ EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
+ EnforcePrecondition(false, order_position >= 0 && order_position <= ::GetVehicle(vehicle_id)->GetNumOrders());
+ EnforcePrecondition(false, AreOrderFlagsValid(destination, order_flags));
+
+ Order order;
+ switch (::GetOrderTypeByTile(destination)) {
+ case OT_GOTO_DEPOT:
+ order.MakeGoToDepot(::GetDepotByTile(destination)->index, (OrderDepotTypeFlags)(ODTFB_PART_OF_ORDERS | ((order_flags & AIOF_SERVICE_IF_NEEDED) ? ODTFB_SERVICE : 0)));
+ break;
+
+ case OT_GOTO_STATION:
+ order.MakeGoToStation(::GetStationIndex(destination));
+ order.SetLoadType((OrderLoadFlags)GB(order_flags, 5, 3));
+ order.SetUnloadType((OrderUnloadFlags)GB(order_flags, 2, 3));
+ break;
+
+ case OT_GOTO_WAYPOINT:
+ order.MakeGoToWaypoint(::GetWaypointIndex(destination));
+ break;
+
+ default:
+ return false;
+ }
+
+ order.SetNonStopType((OrderNonStopFlags)GB(order_flags, 0, 2));
+
+ return AIObject::DoCommand(0, vehicle_id | (order_position << 16), order.Pack(), CMD_INSERT_ORDER);
+}
+
+/* static */ bool AIOrder::RemoveOrder(VehicleID vehicle_id, OrderPosition order_position)
+{
+ order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position);
+
+ EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position));
+
+ return AIObject::DoCommand(0, vehicle_id, order_position, CMD_DELETE_ORDER);
+}
+
+/**
+ * Callback handler as ChangeOrder possibly needs multiple DoCommand calls
+ * to be able to set all order flags correctly. As we need to wait till the
+ * command has completed before we know the next bits to change we need to
+ * call the function multiple times. Each time it'll reduce the difference
+ * between the wanted and the current order.
+ * @param instance The AI we are doing the callback for.
+ */
+static void _DoCommandReturnChangeOrder(class AIInstance *instance)
+{
+ AIObject::SetLastCommandRes(AIOrder::_ChangeOrder());
+ AIInstance::DoCommandReturn(instance);
+}
+
+/* static */ bool AIOrder::_ChangeOrder()
+{
+ /* Make sure we don't go into an infinite loop */
+ int retry = AIObject::GetCallbackVariable(3) - 1;
+ if (retry < 0) {
+ DEBUG(ai, 0, "Possible infinite loop in ChangeOrder detected");
+ return false;
+ }
+ AIObject::SetCallbackVariable(3, retry);
+
+ VehicleID vehicle_id = (VehicleID)AIObject::GetCallbackVariable(0);
+ OrderPosition order_position = (OrderPosition)AIObject::GetCallbackVariable(1);
+ AIOrderFlags order_flags = (AIOrderFlags)AIObject::GetCallbackVariable(2);
+
+ order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position);
+
+ EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position));
+ EnforcePrecondition(false, AreOrderFlagsValid(GetOrderDestination(vehicle_id, order_position), order_flags));
+
+ Order *order = ::GetVehicle(vehicle_id)->GetFirstOrder();
+ for (int i = 0; i < order_position; i++) order = order->next;
+
+ AIOrderFlags current = GetOrderFlags(vehicle_id, order_position);
+
+ if ((current & AIOF_NON_STOP_FLAGS) != (order_flags & AIOF_NON_STOP_FLAGS)) {
+ return AIObject::DoCommand(0, vehicle_id | (order_position << 16), (order_flags & AIOF_NON_STOP_FLAGS) << 4 | MOF_NON_STOP, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnChangeOrder);
+ }
+
+ switch (order->GetType()) {
+ case OT_GOTO_DEPOT:
+ if ((current & AIOF_SERVICE_IF_NEEDED) != (order_flags & AIOF_SERVICE_IF_NEEDED)) {
+ return AIObject::DoCommand(0, vehicle_id | (order_position << 16), MOF_DEPOT_ACTION, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnChangeOrder);
+ }
+ break;
+
+ case OT_GOTO_STATION:
+ if ((current & AIOF_UNLOAD_FLAGS) != (order_flags & AIOF_UNLOAD_FLAGS)) {
+ return AIObject::DoCommand(0, vehicle_id | (order_position << 16), (order_flags & AIOF_UNLOAD_FLAGS) << 2 | MOF_UNLOAD, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnChangeOrder);
+ }
+ if ((current & AIOF_LOAD_FLAGS) != (order_flags & AIOF_LOAD_FLAGS)) {
+ return AIObject::DoCommand(0, vehicle_id | (order_position << 16), (order_flags & AIOF_LOAD_FLAGS) >> 1 | MOF_LOAD, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnChangeOrder);
+ }
+ break;
+
+ default: break;
+ }
+
+ assert(GetOrderFlags(vehicle_id, order_position) == order_flags);
+
+ return true;
+}
+
+/* static */ bool AIOrder::ChangeOrder(VehicleID vehicle_id, OrderPosition order_position, AIOrder::AIOrderFlags order_flags)
+{
+ AIObject::SetCallbackVariable(0, vehicle_id);
+ AIObject::SetCallbackVariable(1, order_position);
+ AIObject::SetCallbackVariable(2, order_flags);
+ /* In case another client(s) change orders at the same time we could
+ * end in an infinite loop. This stops that from happening ever. */
+ AIObject::SetCallbackVariable(3, 8);
+ return AIOrder::_ChangeOrder();
+}
+
+/* static */ bool AIOrder::MoveOrder(VehicleID vehicle_id, OrderPosition order_position_move, OrderPosition order_position_target)
+{
+ order_position_move = AIOrder::ResolveOrderPosition(vehicle_id, order_position_move);
+ order_position_target = AIOrder::ResolveOrderPosition(vehicle_id, order_position_target);
+
+ EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position_move));
+ EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position_target));
+
+ return AIObject::DoCommand(0, vehicle_id, order_position_move | (order_position_target << 16), CMD_MOVE_ORDER);
+}
+
+/* static */ bool AIOrder::CopyOrders(VehicleID vehicle_id, VehicleID main_vehicle_id)
+{
+ EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
+ EnforcePrecondition(false, AIVehicle::IsValidVehicle(main_vehicle_id));
+
+ return AIObject::DoCommand(0, vehicle_id | (main_vehicle_id << 16), CO_COPY, CMD_CLONE_ORDER);
+}
+
+/* static */ bool AIOrder::ShareOrders(VehicleID vehicle_id, VehicleID main_vehicle_id)
+{
+ EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
+ EnforcePrecondition(false, AIVehicle::IsValidVehicle(main_vehicle_id));
+
+ return AIObject::DoCommand(0, vehicle_id | (main_vehicle_id << 16), CO_SHARE, CMD_CLONE_ORDER);
+}
+
+/* static */ bool AIOrder::UnshareOrders(VehicleID vehicle_id)
+{
+ EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
+
+ return AIObject::DoCommand(0, vehicle_id, CO_UNSHARE, CMD_CLONE_ORDER);
+}
diff --git a/src/ai/api/ai_order.hpp b/src/ai/api/ai_order.hpp
new file mode 100644
index 000000000..fbda26892
--- /dev/null
+++ b/src/ai/api/ai_order.hpp
@@ -0,0 +1,253 @@
+/* $Id$ */
+
+/** @file ai_order.hpp Everything to query and build orders. */
+
+#ifndef AI_ORDER_HPP
+#define AI_ORDER_HPP
+
+#include "ai_object.hpp"
+#include "ai_error.hpp"
+
+/**
+ * Class that handles all order related functions.
+ */
+class AIOrder : public AIObject {
+public:
+ static const char *GetClassName() { return "AIOrder"; }
+
+ /**
+ * All order related error messages.
+ */
+ enum ErrorMessages {
+ /** Base for all order related errors */
+ ERR_ORDER_BASE = AIError::ERR_CAT_ORDER << AIError::ERR_CAT_BIT_SIZE,
+
+ /** No more space for orders */
+ ERR_ORDER_TOO_MANY, // [STR_8831_NO_MORE_SPACE_FOR_ORDERS]
+
+ /** Destination of new order is to far away from the previous order */
+ ERR_ORDER_TOO_FAR_AWAY_FROM_PREVIOUS_DESTINATION, // [STR_0210_TOO_FAR_FROM_PREVIOUS_DESTINATIO]
+ };
+
+ /**
+ * Flags that can be used to modify the behaviour of orders.
+ */
+ enum AIOrderFlags {
+ /** Just go to the station/depot, stop unload if possible and load if needed. */
+ AIOF_NONE = 0,
+
+ /** Do not stop at the stations that are passed when going to the destination. Only for trains and road vehicles. */
+ AIOF_NON_STOP_INTERMEDIATE = 1 << 0,
+ /** Do not stop at the destionation station. Only for trains and road vehicles. */
+ AIOF_NON_STOP_DESTINATION = 1 << 1,
+
+ /** Always unload the vehicle; only for stations. Cannot be set when AIOF_TRANSFER or AIOF_NO_UNLOAD is set. */
+ AIOF_UNLOAD = 1 << 2,
+ /** Transfer instead of deliver the goods; only for stations. Cannot be set when AIOF_UNLOAD or AIOF_NO_UNLOAD is set. */
+ AIOF_TRANSFER = 1 << 3,
+ /** Never unload the vehicle; only for stations. Cannot be set when AIOF_UNLOAD, AIOF_TRANSFER or AIOF_NO_LOAD is set. */
+ AIOF_NO_UNLOAD = 1 << 4,
+
+ /** Wait till the vehicle is fully loaded; only for stations. Cannot be set when AIOF_NO_LOAD is set. */
+ AIOF_FULL_LOAD = 2 << 5,
+ /** Wait till at least one cargo of the vehicle is fully loaded; only for stations. Cannot be set when AIOF_NO_LOAD is set. */
+ AIOF_FULL_LOAD_ANY = 3 << 5,
+ /** Do not load any cargo; only for stations. Cannot be set when AIOF_NO_UNLOAD, AIOF_FULL_LOAD or AIOF_FULL_LOAD_ANY is set. */
+ AIOF_NO_LOAD = 1 << 7,
+
+ /** Service the vehicle when needed, otherwise skip this order; only for depots. */
+ AIOF_SERVICE_IF_NEEDED = 1 << 2,
+
+ /** All flags related to non-stop settings. */
+ AIOF_NON_STOP_FLAGS = AIOF_NON_STOP_INTERMEDIATE | AIOF_NON_STOP_DESTINATION,
+ /** All flags related to unloading. */
+ AIOF_UNLOAD_FLAGS = AIOF_TRANSFER | AIOF_UNLOAD | AIOF_NO_UNLOAD,
+ /** All flags related to loading. */
+ AIOF_LOAD_FLAGS = AIOF_FULL_LOAD | AIOF_FULL_LOAD_ANY | AIOF_NO_LOAD,
+
+ /** For marking invalid order flags */
+ AIOF_INVALID = 0xFFFF,
+ };
+
+ /** Different constants related to the OrderPosition */
+ enum OrderPosition {
+ CURRENT_ORDER = 0xFF, //!< Constant that gets resolved to the current order.
+ INVALID_ORDER = -1, //!< An invalid order.
+ };
+
+ /**
+ * Checks whether the given order id is valid for the given vehicle.
+ * @param vehicle_id The vehicle to check the order index for.
+ * @param order_position The order index to check.
+ * @pre AIVehicle::IsValidVehicle(vehicle_id).
+ * @return True if and only if the order_position is valid for the given vehicle.
+ */
+ static bool IsValidVehicleOrder(VehicleID vehicle_id, OrderPosition order_position);
+
+ /**
+ * Resolves the given order index to the correct index for the given vehicle.
+ * If the order index was CURRENT_ORDER it will be resolved to the index of
+ * the current order (as shown in the order list). If the order with the
+ * given index does not exist it will return INVALID_ORDER.
+ * @param vehicle_id The vehicle to check the order index for.
+ * @param order_position The order index to resolve.
+ * @pre AIVehicle::IsValidVehicle(vehicle_id).
+ * @return The resolved order index.
+ */
+ static OrderPosition ResolveOrderPosition(VehicleID vehicle_id, OrderPosition order_position);
+
+ /**
+ * Checks whether the given order flags are valid for the given destination.
+ * @param destination The destination of the order.
+ * @param order_flags The flags given to the order.
+ * @return True if and only if the order_flags are valid for the given location.
+ */
+ static bool AreOrderFlagsValid(TileIndex destination, AIOrderFlags order_flags);
+
+ /**
+ * Returns the number of orders for the given vehicle.
+ * @param vehicle_id The vehicle to get the order count of.
+ * @pre AIVehicle::IsValidVehicle(vehicle_id).
+ * @return The number of orders for the given vehicle or a negative
+ * value when the vehicle does not exist.
+ */
+ static int32 GetOrderCount(VehicleID vehicle_id);
+
+ /**
+ * Gets the destination of the given order for the given vehicle.
+ * @param vehicle_id The vehicle to get the destination for.
+ * @param order_position The order to get the destination for.
+ * @pre IsValidVehicleOrder(vehicle_id, order_position).
+ * @note Giving CURRENT_ORDER as order_position will give the order that is
+ * currently being executed by the vehicle. This is not necessarily the
+ * current order as given by ResolveOrderPosition (the current index in the
+ * order list) as manual or autoservicing depot orders do not show up
+ * in the orderlist, but they can be the current order of a vehicle.
+ * @return The destination tile of the order.
+ */
+ static TileIndex GetOrderDestination(VehicleID vehicle_id, OrderPosition order_position);
+
+ /**
+ * Gets the AIOrderFlags of the given order for the given vehicle.
+ * @param vehicle_id The vehicle to get the destination for.
+ * @param order_position The order to get the destination for.
+ * @pre IsValidVehicleOrder(vehicle_id, order_position).
+ * @note Giving CURRENT_ORDER as order_position will give the order that is
+ * currently being executed by the vehicle. This is not necessarily the
+ * current order as given by ResolveOrderPosition (the current index in the
+ * order list) as manual or autoservicing depot orders do not show up
+ * in the orderlist, but they can be the current order of a vehicle.
+ * @return The AIOrderFlags of the order.
+ */
+ static AIOrderFlags GetOrderFlags(VehicleID vehicle_id, OrderPosition order_position);
+
+ /**
+ * Appends an order to the end of the vehicle's order list.
+ * @param vehicle_id The vehicle to append the order to.
+ * @param destination The destination of the order.
+ * @param order_flags The flags given to the order.
+ * @pre AIVehicle::IsValidVehicle(vehicle_id).
+ * @pre AreOrderFlagsValid(destination, order_flags).
+ * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
+ * @exception AIOrder::ERR_ORDER_NO_MORE_SPACE
+ * @exception AIOrder::ERR_ORDER_TOO_FAR_AWAY_FROM_PREVIOUS_DESTINATION
+ * @return True if and only if the order was appended.
+ */
+ static bool AppendOrder(VehicleID vehicle_id, TileIndex destination, AIOrderFlags order_flags);
+
+ /**
+ * Inserts an order before the given order_position into the vehicle's order list.
+ * @param vehicle_id The vehicle to add the order to.
+ * @param order_position The order to place the new order before.
+ * @param destination The destination of the order.
+ * @param order_flags The flags given to the order.
+ * @pre IsValidVehicleOrder(vehicle_id, order_position).
+ * @pre AreOrderFlagsValid(destination, order_flags).
+ * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
+ * @exception AIOrder::ERR_ORDER_NO_MORE_SPACE
+ * @exception AIOrder::ERR_ORDER_TOO_FAR_AWAY_FROM_PREVIOUS_DESTINATION
+ * @return True if and only if the order was inserted.
+ */
+ static bool InsertOrder(VehicleID vehicle_id, OrderPosition order_position, TileIndex destination, AIOrderFlags order_flags);
+
+ /**
+ * Removes an order from the vehicle's order list.
+ * @param vehicle_id The vehicle to remove the order from.
+ * @param order_position The order to remove from the order list.
+ * @pre AIVehicle::IsValidVehicleOrder(vehicle_id, order_position).
+ * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
+ * @return True if and only if the order was removed.
+ */
+ static bool RemoveOrder(VehicleID vehicle_id, OrderPosition order_position);
+
+#ifndef DOXYGEN_SKIP
+ /**
+ * Internal function to help ChangeOrder.
+ */
+ static bool _ChangeOrder();
+#endif
+
+ /**
+ * Changes the order flags of the given order.
+ * @param vehicle_id The vehicle to change the order of.
+ * @param order_position The order to change.
+ * @param order_flags The new flags given to the order.
+ * @pre IsValidVehicleOrder(vehicle_id, order_position).
+ * @pre AreOrderFlagsValid(GetOrderDestination(vehicle_id, order_position), order_flags).
+ * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
+ * @return True if and only if the order was changed.
+ */
+ static bool ChangeOrder(VehicleID vehicle_id, OrderPosition order_position, AIOrderFlags order_flags);
+
+ /**
+ * Move an order inside the orderlist
+ * @param vehicle_id The vehicle to move the orders.
+ * @param order_position_move The order to move.
+ * @param order_position_target The target order
+ * @pre IsValidVehicleOrder(vehicle_id, order_position_move).
+ * @pre IsValidVehicleOrder(vehicle_id, order_position_target).
+ * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
+ * @return True if and only if the order was moved.
+ * @note If the order is moved to a lower place (e.g. from 7 to 2)
+ * the target order is moved upwards (e.g. 3). If the order is moved
+ * to a higher place (e.g. from 7 to 9) the target will be moved
+ * downwards (e.g. 8).
+ */
+ static bool MoveOrder(VehicleID vehicle_id, OrderPosition order_position_move, OrderPosition order_position_target);
+
+ /**
+ * Copies the orders from another vehicle. The orders of the main vehicle
+ * are going to be the orders of the changed vehicle.
+ * @param vehicle_id The vehicle to copy the orders to.
+ * @param main_vehicle_id The vehicle to copy the orders from.
+ * @pre AIVehicle::IsValidVehicle(vehicle_id).
+ * @pre AIVehicle::IsValidVehicle(main_vehicle_id).
+ * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
+ * @exception AIOrder::ERR_ORDER_NO_MORE_SPACE
+ * @return True if and only if the copying succeeded.
+ */
+ static bool CopyOrders(VehicleID vehicle_id, VehicleID main_vehicle_id);
+
+ /**
+ * Shares the orders between two vehicles. The orders of the main
+ * vehicle are going to be the orders of the changed vehicle.
+ * @param vehicle_id The vehicle to add to the shared order list.
+ * @param main_vehicle_id The vehicle to share the orders with.
+ * @pre AIVehicle::IsValidVehicle(vehicle_id).
+ * @pre AIVehicle::IsValidVehicle(main_vehicle_id).
+ * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
+ * @return True if and only if the sharing succeeded.
+ */
+ static bool ShareOrders(VehicleID vehicle_id, VehicleID main_vehicle_id);
+
+ /**
+ * Removes the given vehicle from a shared orders list.
+ * @param vehicle_id The vehicle to remove from the shared order list.
+ * @pre AIVehicle::IsValidVehicle(vehicle_id).
+ * @return True if and only if the unsharing succeeded.
+ */
+ static bool UnshareOrders(VehicleID vehicle_id);
+};
+DECLARE_ENUM_AS_BIT_SET(AIOrder::AIOrderFlags);
+
+#endif /* AI_ORDER_HPP */
diff --git a/src/ai/api/ai_order.hpp.sq b/src/ai/api/ai_order.hpp.sq
new file mode 100644
index 000000000..4128ae029
--- /dev/null
+++ b/src/ai/api/ai_order.hpp.sq
@@ -0,0 +1,71 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_order.hpp"
+
+namespace SQConvert {
+ /* Allow enums to be used as Squirrel parameters */
+ template <> AIOrder::ErrorMessages GetParam(ForceType<AIOrder::ErrorMessages>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIOrder::ErrorMessages)tmp; }
+ template <> int Return<AIOrder::ErrorMessages>(HSQUIRRELVM vm, AIOrder::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; }
+ template <> AIOrder::AIOrderFlags GetParam(ForceType<AIOrder::AIOrderFlags>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIOrder::AIOrderFlags)tmp; }
+ template <> int Return<AIOrder::AIOrderFlags>(HSQUIRRELVM vm, AIOrder::AIOrderFlags res) { sq_pushinteger(vm, (int32)res); return 1; }
+ template <> AIOrder::OrderPosition GetParam(ForceType<AIOrder::OrderPosition>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIOrder::OrderPosition)tmp; }
+ template <> int Return<AIOrder::OrderPosition>(HSQUIRRELVM vm, AIOrder::OrderPosition res) { sq_pushinteger(vm, (int32)res); return 1; }
+
+ /* Allow AIOrder to be used as Squirrel parameter */
+ template <> AIOrder *GetParam(ForceType<AIOrder *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIOrder *)instance; }
+ template <> AIOrder &GetParam(ForceType<AIOrder &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIOrder *)instance; }
+ template <> const AIOrder *GetParam(ForceType<const AIOrder *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIOrder *)instance; }
+ template <> const AIOrder &GetParam(ForceType<const AIOrder &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIOrder *)instance; }
+ template <> int Return<AIOrder *>(HSQUIRRELVM vm, AIOrder *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIOrder", res, NULL, DefSQDestructorCallback<AIOrder>); return 1; }
+}; // namespace SQConvert
+
+void SQAIOrder_Register(Squirrel *engine) {
+ DefSQClass <AIOrder> SQAIOrder("AIOrder");
+ SQAIOrder.PreRegister(engine);
+ SQAIOrder.AddConstructor<void (AIOrder::*)(), 1>(engine, "x");
+
+ SQAIOrder.DefSQConst(engine, AIOrder::ERR_ORDER_BASE, "ERR_ORDER_BASE");
+ SQAIOrder.DefSQConst(engine, AIOrder::ERR_ORDER_TOO_MANY, "ERR_ORDER_TOO_MANY");
+ SQAIOrder.DefSQConst(engine, AIOrder::ERR_ORDER_TOO_FAR_AWAY_FROM_PREVIOUS_DESTINATION, "ERR_ORDER_TOO_FAR_AWAY_FROM_PREVIOUS_DESTINATION");
+ SQAIOrder.DefSQConst(engine, AIOrder::AIOF_NONE, "AIOF_NONE");
+ SQAIOrder.DefSQConst(engine, AIOrder::AIOF_NON_STOP_INTERMEDIATE, "AIOF_NON_STOP_INTERMEDIATE");
+ SQAIOrder.DefSQConst(engine, AIOrder::AIOF_NON_STOP_DESTINATION, "AIOF_NON_STOP_DESTINATION");
+ SQAIOrder.DefSQConst(engine, AIOrder::AIOF_UNLOAD, "AIOF_UNLOAD");
+ SQAIOrder.DefSQConst(engine, AIOrder::AIOF_TRANSFER, "AIOF_TRANSFER");
+ SQAIOrder.DefSQConst(engine, AIOrder::AIOF_NO_UNLOAD, "AIOF_NO_UNLOAD");
+ SQAIOrder.DefSQConst(engine, AIOrder::AIOF_FULL_LOAD, "AIOF_FULL_LOAD");
+ SQAIOrder.DefSQConst(engine, AIOrder::AIOF_FULL_LOAD_ANY, "AIOF_FULL_LOAD_ANY");
+ SQAIOrder.DefSQConst(engine, AIOrder::AIOF_NO_LOAD, "AIOF_NO_LOAD");
+ SQAIOrder.DefSQConst(engine, AIOrder::AIOF_SERVICE_IF_NEEDED, "AIOF_SERVICE_IF_NEEDED");
+ SQAIOrder.DefSQConst(engine, AIOrder::AIOF_NON_STOP_FLAGS, "AIOF_NON_STOP_FLAGS");
+ SQAIOrder.DefSQConst(engine, AIOrder::AIOF_UNLOAD_FLAGS, "AIOF_UNLOAD_FLAGS");
+ SQAIOrder.DefSQConst(engine, AIOrder::AIOF_LOAD_FLAGS, "AIOF_LOAD_FLAGS");
+ SQAIOrder.DefSQConst(engine, AIOrder::AIOF_INVALID, "AIOF_INVALID");
+ SQAIOrder.DefSQConst(engine, AIOrder::CURRENT_ORDER, "CURRENT_ORDER");
+ SQAIOrder.DefSQConst(engine, AIOrder::INVALID_ORDER, "INVALID_ORDER");
+
+ AIError::RegisterErrorMap(STR_8831_NO_MORE_SPACE_FOR_ORDERS, AIOrder::ERR_ORDER_TOO_MANY);
+ AIError::RegisterErrorMap(STR_0210_TOO_FAR_FROM_PREVIOUS_DESTINATIO, AIOrder::ERR_ORDER_TOO_FAR_AWAY_FROM_PREVIOUS_DESTINATION);
+
+ AIError::RegisterErrorMapString(AIOrder::ERR_ORDER_TOO_MANY, "ERR_ORDER_TOO_MANY");
+ AIError::RegisterErrorMapString(AIOrder::ERR_ORDER_TOO_FAR_AWAY_FROM_PREVIOUS_DESTINATION, "ERR_ORDER_TOO_FAR_AWAY_FROM_PREVIOUS_DESTINATION");
+
+ SQAIOrder.DefSQStaticMethod(engine, &AIOrder::GetClassName, "GetClassName", 1, "x");
+ SQAIOrder.DefSQStaticMethod(engine, &AIOrder::IsValidVehicleOrder, "IsValidVehicleOrder", 3, "xii");
+ SQAIOrder.DefSQStaticMethod(engine, &AIOrder::ResolveOrderPosition, "ResolveOrderPosition", 3, "xii");
+ SQAIOrder.DefSQStaticMethod(engine, &AIOrder::AreOrderFlagsValid, "AreOrderFlagsValid", 3, "xii");
+ SQAIOrder.DefSQStaticMethod(engine, &AIOrder::GetOrderCount, "GetOrderCount", 2, "xi");
+ SQAIOrder.DefSQStaticMethod(engine, &AIOrder::GetOrderDestination, "GetOrderDestination", 3, "xii");
+ SQAIOrder.DefSQStaticMethod(engine, &AIOrder::GetOrderFlags, "GetOrderFlags", 3, "xii");
+ SQAIOrder.DefSQStaticMethod(engine, &AIOrder::AppendOrder, "AppendOrder", 4, "xiii");
+ SQAIOrder.DefSQStaticMethod(engine, &AIOrder::InsertOrder, "InsertOrder", 5, "xiiii");
+ SQAIOrder.DefSQStaticMethod(engine, &AIOrder::RemoveOrder, "RemoveOrder", 3, "xii");
+ SQAIOrder.DefSQStaticMethod(engine, &AIOrder::ChangeOrder, "ChangeOrder", 4, "xiii");
+ SQAIOrder.DefSQStaticMethod(engine, &AIOrder::MoveOrder, "MoveOrder", 4, "xiii");
+ SQAIOrder.DefSQStaticMethod(engine, &AIOrder::CopyOrders, "CopyOrders", 3, "xii");
+ SQAIOrder.DefSQStaticMethod(engine, &AIOrder::ShareOrders, "ShareOrders", 3, "xii");
+ SQAIOrder.DefSQStaticMethod(engine, &AIOrder::UnshareOrders, "UnshareOrders", 2, "xi");
+
+ SQAIOrder.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_rail.cpp b/src/ai/api/ai_rail.cpp
new file mode 100644
index 000000000..f35f98de3
--- /dev/null
+++ b/src/ai/api/ai_rail.cpp
@@ -0,0 +1,442 @@
+/* $Id$ */
+
+/** @file ai_rail.cpp Implementation of AIRail. */
+
+#include "ai_rail.hpp"
+#include "ai_object.hpp"
+#include "ai_map.hpp"
+#include "../../openttd.h"
+#include "../../debug.h"
+#include "../../rail_map.h"
+#include "../../road_map.h"
+#include "../../command_type.h"
+#include "../../station_map.h"
+#include "../../company_func.h"
+#include "../../core/math_func.hpp"
+#include "../../waypoint.h"
+#include "../../newgrf.h"
+#include "../../newgrf_generic.h"
+#include "../../newgrf_station.h"
+#include "../../newgrf_callbacks.h"
+
+/* static */ bool AIRail::IsRailTile(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return false;
+
+ return (::IsTileType(tile, MP_RAILWAY) && !::IsRailDepot(tile)) ||
+ (::IsRailwayStationTile(tile) && !::IsStationTileBlocked(tile)) || ::IsLevelCrossingTile(tile);
+}
+
+/* static */ bool AIRail::IsLevelCrossingTile(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return false;
+
+ return ::IsLevelCrossingTile(tile);
+}
+
+/* static */ bool AIRail::IsRailDepotTile(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return false;
+
+ return ::IsRailDepotTile(tile);
+}
+
+/* static */ bool AIRail::IsRailStationTile(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return false;
+
+ return ::IsRailwayStationTile(tile);
+}
+
+/* static */ bool AIRail::IsRailWaypointTile(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return false;
+
+ return ::IsTileType(tile, MP_RAILWAY) && ::IsRailWaypointTile(tile);
+}
+
+/* static */ bool AIRail::IsRailTypeAvailable(RailType rail_type)
+{
+ if ((::RailType)rail_type < RAILTYPE_BEGIN || (::RailType)rail_type >= RAILTYPE_END) return false;
+
+ return ::HasRailtypeAvail(_current_company, (::RailType)rail_type);
+}
+
+/* static */ AIRail::RailType AIRail::GetCurrentRailType()
+{
+ return (RailType)AIObject::GetRailType();
+}
+
+/* static */ void AIRail::SetCurrentRailType(RailType rail_type)
+{
+ if (!IsRailTypeAvailable(rail_type)) return;
+
+ AIObject::SetRailType((::RailType)rail_type);
+}
+
+/* static */ bool AIRail::TrainCanRunOnRail(AIRail::RailType engine_rail_type, AIRail::RailType track_rail_type)
+{
+ if (!AIRail::IsRailTypeAvailable(engine_rail_type)) return false;
+ if (!AIRail::IsRailTypeAvailable(track_rail_type)) return false;
+
+ return ::IsCompatibleRail((::RailType)engine_rail_type, (::RailType)track_rail_type);
+}
+
+/* static */ bool AIRail::TrainHasPowerOnRail(AIRail::RailType engine_rail_type, AIRail::RailType track_rail_type)
+{\
+ if (!AIRail::IsRailTypeAvailable(engine_rail_type)) return false;
+ if (!AIRail::IsRailTypeAvailable(track_rail_type)) return false;
+
+ return ::HasPowerOnRail((::RailType)engine_rail_type, (::RailType)track_rail_type);
+}
+
+/* static */ AIRail::RailType AIRail::GetRailType(TileIndex tile)
+{
+ if (!AITile::HasTransportType(tile, AITile::TRANSPORT_RAIL)) return RAILTYPE_INVALID;
+
+ return (RailType)::GetRailType(tile);
+}
+
+/* static */ bool AIRail::ConvertRailType(TileIndex start_tile, TileIndex end_tile, AIRail::RailType convert_to)
+{
+ EnforcePrecondition(false, ::IsValidTile(start_tile));
+ EnforcePrecondition(false, ::IsValidTile(end_tile));
+ EnforcePrecondition(false, IsRailTypeAvailable(convert_to));
+
+ return AIObject::DoCommand(start_tile, end_tile, convert_to, CMD_CONVERT_RAIL);
+}
+
+/* static */ TileIndex AIRail::GetRailDepotFrontTile(TileIndex depot)
+{
+ if (!IsRailDepotTile(depot)) return INVALID_TILE;
+
+ return depot + ::TileOffsByDiagDir(::GetRailDepotDirection(depot));
+}
+
+/* static */ AIRail::RailTrack AIRail::GetRailStationDirection(TileIndex tile)
+{
+ if (!IsRailStationTile(tile)) return RAILTRACK_INVALID;
+
+ return (RailTrack)::GetRailStationTrack(tile);
+}
+
+/* static */ bool AIRail::BuildRailDepot(TileIndex tile, TileIndex front)
+{
+ EnforcePrecondition(false, tile != front);
+ EnforcePrecondition(false, ::IsValidTile(tile));
+ EnforcePrecondition(false, ::IsValidTile(front));
+ EnforcePrecondition(false, ::TileX(tile) == ::TileX(front) || ::TileY(tile) == ::TileY(front));
+ EnforcePrecondition(false, IsRailTypeAvailable(GetCurrentRailType()));
+
+ uint entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? 1 : 3) : (::TileX(tile) < ::TileX(front) ? 2 : 0);
+
+ return AIObject::DoCommand(tile, AIObject::GetRailType(), entrance_dir, CMD_BUILD_TRAIN_DEPOT);
+}
+
+/* static */ bool AIRail::BuildRailStation(TileIndex tile, RailTrack direction, uint num_platforms, uint platform_length, bool join_adjacent)
+{
+ EnforcePrecondition(false, ::IsValidTile(tile));
+ EnforcePrecondition(false, direction == RAILTRACK_NW_SE || direction == RAILTRACK_NE_SW);
+ EnforcePrecondition(false, num_platforms > 0 && num_platforms <= 0xFF);
+ EnforcePrecondition(false, platform_length > 0 && platform_length <= 0xFF);
+ EnforcePrecondition(false, IsRailTypeAvailable(GetCurrentRailType()));
+
+ uint32 p1 = GetCurrentRailType() | (platform_length << 16) | (num_platforms << 8);
+ if (direction == RAILTRACK_NW_SE) p1 |= (1 << 4);
+ if (!join_adjacent) p1 |= (1 << 24);
+ return AIObject::DoCommand(tile, p1, INVALID_STATION << 16, CMD_BUILD_RAILROAD_STATION);
+}
+
+/* static */ bool AIRail::BuildNewGRFRailStation(TileIndex tile, RailTrack direction, uint num_platforms, uint platform_length, bool join_adjacent, CargoID cargo_id, IndustryType source_industry, IndustryType goal_industry, int distance, bool source_station)
+{
+ EnforcePrecondition(false, ::IsValidTile(tile));
+ EnforcePrecondition(false, direction == RAILTRACK_NW_SE || direction == RAILTRACK_NE_SW);
+ EnforcePrecondition(false, num_platforms > 0 && num_platforms <= 0xFF);
+ EnforcePrecondition(false, platform_length > 0 && platform_length <= 0xFF);
+ EnforcePrecondition(false, IsRailTypeAvailable(GetCurrentRailType()));
+
+ uint32 p1 = GetCurrentRailType() | (platform_length << 16) | (num_platforms << 8);
+ if (direction == RAILTRACK_NW_SE) p1 |= 1 << 4;
+ if (!join_adjacent) p1 |= (1 << 24);
+
+ const GRFFile *file;
+ uint16 res = GetAiPurchaseCallbackResult(GSF_STATION, cargo_id, 0, source_industry, goal_industry, min(255, distance / 2), AICE_STATION_GET_STATION_ID, source_station ? 0 : 1, min(15, num_platforms) << 4 | min(15, platform_length), &file);
+ uint32 p2 = INVALID_STATION << 16;
+ if (res != CALLBACK_FAILED) {
+ extern StationClass _station_classes[STAT_CLASS_MAX];
+ const StationSpec *spec = GetCustomStationSpecByGrf(file->grfid, res);
+ int index = -1;
+ for (uint j = 0; j < _station_classes[spec->sclass].stations; j++) {
+ if (spec == _station_classes[spec->sclass].spec[j]) {
+ index = j;
+ break;
+ }
+ }
+ if (index == -1) {
+ DEBUG(grf, 1, "%s returned an invalid station ID for 'AI construction/purchase selection (18)' callback", file->filename);
+ } else {
+ p2 |= spec->sclass | index << 8;
+ }
+
+ }
+ return AIObject::DoCommand(tile, p1, p2, CMD_BUILD_RAILROAD_STATION);
+}
+
+/* static */ bool AIRail::RemoveRailStationTileRect(TileIndex tile, TileIndex tile2)
+{
+ EnforcePrecondition(false, ::IsValidTile(tile));
+ EnforcePrecondition(false, ::IsValidTile(tile2));
+
+ return AIObject::DoCommand(tile, tile2, 0, CMD_REMOVE_FROM_RAILROAD_STATION);
+}
+
+/* static */ uint AIRail::GetRailTracks(TileIndex tile)
+{
+ if (!IsRailTile(tile)) return RAILTRACK_INVALID;
+
+ if (IsRailWaypointTile(tile)) return ::GetRailWaypointBits(tile);
+ if (IsRailStationTile(tile)) return ::TrackToTrackBits(::GetRailStationTrack(tile));
+ if (IsLevelCrossingTile(tile)) return ::GetCrossingRailBits(tile);
+ return ::GetTrackBits(tile);
+}
+
+/* static */ bool AIRail::BuildRailTrack(TileIndex tile, RailTrack rail_track)
+{
+ EnforcePrecondition(false, ::IsValidTile(tile));
+ EnforcePrecondition(false, rail_track != 0);
+ EnforcePrecondition(false, (rail_track & ~::TRACK_BIT_ALL) == 0);
+ EnforcePrecondition(false, KillFirstBit((uint)rail_track) == 0);
+ EnforcePrecondition(false, IsRailTypeAvailable(GetCurrentRailType()));
+
+ return AIObject::DoCommand(tile, tile, GetCurrentRailType() | (FindFirstTrack((::TrackBits)rail_track) << 4), CMD_BUILD_RAILROAD_TRACK);
+}
+
+/* static */ bool AIRail::RemoveRailTrack(TileIndex tile, RailTrack rail_track)
+{
+ EnforcePrecondition(false, ::IsValidTile(tile));
+ EnforcePrecondition(false, ::IsTileType(tile, MP_RAILWAY) && ::IsPlainRailTile(tile));
+ EnforcePrecondition(false, GetRailTracks(tile) & rail_track);
+ EnforcePrecondition(false, KillFirstBit((uint)rail_track) == 0);
+
+ return AIObject::DoCommand(tile, tile, GetCurrentRailType() | (FindFirstTrack((::TrackBits)rail_track) << 4), CMD_REMOVE_RAILROAD_TRACK);
+}
+
+/* static */ bool AIRail::AreTilesConnected(TileIndex from, TileIndex tile, TileIndex to)
+{
+ if (!IsRailTile(tile)) return false;
+ if (from == to || AIMap::DistanceManhattan(from, tile) != 1 || AIMap::DistanceManhattan(tile, to) != 1) return false;
+
+ if (to < from) ::Swap(from, to);
+
+ if (tile - from == 1) {
+ if (to - tile == 1) return (GetRailTracks(tile) & RAILTRACK_NE_SW) != 0;
+ if (to - tile == ::MapSizeX()) return (GetRailTracks(tile) & RAILTRACK_NE_SE) != 0;
+ } else if (tile - from == ::MapSizeX()) {
+ if (tile - to == 1) return (GetRailTracks(tile) & RAILTRACK_NW_NE) != 0;
+ if (to - tile == 1) return (GetRailTracks(tile) & RAILTRACK_NW_SW) != 0;
+ if (to - tile == ::MapSizeX()) return (GetRailTracks(tile) & RAILTRACK_NW_SE) != 0;
+ } else {
+ return (GetRailTracks(tile) & RAILTRACK_SW_SE) != 0;
+ }
+
+ NOT_REACHED();
+}
+
+/**
+ * Prepare the second parameter for CmdBuildRailroadTrack and CmdRemoveRailroadTrack. The direction
+ * depends on all three tiles. Sometimes the third tile needs to be adjusted.
+ */
+static uint32 SimulateDrag(TileIndex from, TileIndex tile, TileIndex *to)
+{
+ int diag_offset = abs(abs((int)::TileX(*to) - (int)::TileX(tile)) - abs((int)::TileY(*to) - (int)::TileY(tile)));
+ uint32 p2 = AIRail::GetCurrentRailType();
+ if (::TileY(from) == ::TileY(*to)) {
+ p2 |= (TRACK_X << 4);
+ *to -= Clamp((int)::TileX(*to) - (int)::TileX(tile), -1, 1);
+ } else if (::TileX(from) == ::TileX(*to)) {
+ p2 |= (TRACK_Y << 4);
+ *to -= ::MapSizeX() * Clamp((int)::TileY(*to) - (int)::TileY(tile), -1, 1);
+ } else if (::TileY(from) < ::TileY(tile)) {
+ if (::TileX(*to) < ::TileX(tile)) {
+ p2 |= (TRACK_UPPER << 4);
+ } else {
+ p2 |= (TRACK_LEFT << 4);
+ }
+ if (diag_offset) {
+ *to -= Clamp((int)::TileX(*to) - (int)::TileX(tile), -1, 1);
+ } else {
+ *to -= ::MapSizeX() * Clamp((int)::TileY(*to) - (int)::TileY(tile), -1, 1);
+ }
+ } else if (::TileY(from) > ::TileY(tile)) {
+ if (::TileX(*to) < ::TileX(tile)) {
+ p2 |= (TRACK_RIGHT << 4);
+ } else {
+ p2 |= (TRACK_LOWER << 4);
+ }
+ if (diag_offset) {
+ *to -= Clamp((int)::TileX(*to) - (int)::TileX(tile), -1, 1);
+ } else {
+ *to -= ::MapSizeX() * Clamp((int)::TileY(*to) - (int)::TileY(tile), -1, 1);
+ }
+ } else if (::TileX(from) < ::TileX(tile)) {
+ if (::TileY(*to) < ::TileY(tile)) {
+ p2 |= (TRACK_UPPER << 4);
+ } else {
+ p2 |= (TRACK_RIGHT << 4);
+ }
+ if (!diag_offset) {
+ *to -= Clamp((int)::TileX(*to) - (int)::TileX(tile), -1, 1);
+ } else {
+ *to -= ::MapSizeX() * Clamp((int)::TileY(*to) - (int)::TileY(tile), -1, 1);
+ }
+ } else if (::TileX(from) > ::TileX(tile)) {
+ if (::TileY(*to) < ::TileY(tile)) {
+ p2 |= (TRACK_LEFT << 4);
+ } else {
+ p2 |= (TRACK_LOWER << 4);
+ }
+ if (!diag_offset) {
+ *to -= Clamp((int)::TileX(*to) - (int)::TileX(tile), -1, 1);
+ } else {
+ *to -= ::MapSizeX() * Clamp((int)::TileY(*to) - (int)::TileY(tile), -1, 1);
+ }
+ }
+ return p2;
+}
+
+/* static */ bool AIRail::BuildRail(TileIndex from, TileIndex tile, TileIndex to)
+{
+ EnforcePrecondition(false, ::IsValidTile(from));
+ EnforcePrecondition(false, ::IsValidTile(tile));
+ EnforcePrecondition(false, ::IsValidTile(to));
+ EnforcePrecondition(false, ::DistanceManhattan(from, tile) == 1);
+ EnforcePrecondition(false, ::DistanceManhattan(tile,to) >= 1);
+ EnforcePrecondition(false, IsRailTypeAvailable(GetCurrentRailType()));
+ int diag_offset = abs(abs((int)::TileX(to) - (int)::TileX(tile)) - abs((int)::TileY(to) - (int)::TileY(tile)));
+ EnforcePrecondition(false, diag_offset <= 1 ||
+ (::TileX(from) == ::TileX(tile) && ::TileX(tile) == ::TileX(to)) ||
+ (::TileY(from) == ::TileY(tile) && ::TileY(tile) == ::TileY(to)));
+
+ uint32 p2 = SimulateDrag(from, tile, &to);
+ return AIObject::DoCommand(tile, to, p2, CMD_BUILD_RAILROAD_TRACK);
+}
+
+/* static */ bool AIRail::RemoveRail(TileIndex from, TileIndex tile, TileIndex to)
+{
+ EnforcePrecondition(false, ::IsValidTile(from));
+ EnforcePrecondition(false, ::IsValidTile(tile));
+ EnforcePrecondition(false, ::IsValidTile(to));
+ EnforcePrecondition(false, ::DistanceManhattan(from, tile) == 1);
+ EnforcePrecondition(false, ::DistanceManhattan(tile,to) >= 1);
+ int diag_offset = abs(abs((int)::TileX(to) - (int)::TileX(tile)) - abs((int)::TileY(to) - (int)::TileY(tile)));
+ EnforcePrecondition(false, diag_offset <= 1 ||
+ (::TileX(from) == ::TileX(tile) && ::TileX(tile) == ::TileX(to)) ||
+ (::TileY(from) == ::TileY(tile) && ::TileY(tile) == ::TileY(to)));
+
+ if (!IsRailTypeAvailable(GetCurrentRailType())) SetCurrentRailType(GetRailType(tile));
+ uint32 p2 = SimulateDrag(from, tile, &to);
+ return AIObject::DoCommand(tile, to, p2, CMD_REMOVE_RAILROAD_TRACK);
+}
+
+/**
+ * Contains information about the trackdir that belongs to a track when entering
+ * from a specific direction.
+ */
+struct AIRailSignalData {
+ Track track; //!< The track that will be taken to travel.
+ Trackdir trackdir; //!< The Trackdir belonging to that track.
+ uint signal_cycles; //!< How many times the signal should be cycled in order to build it in the correct direction.
+};
+
+static const int NUM_TRACK_DIRECTIONS = 3; //!< The number of directions you can go when entering a tile.
+
+/**
+ * List information about the trackdir and number of needed cycles for building signals when
+ * entering a track from a specific direction. The first index is the difference between the
+ * TileIndex of the previous and current tile, where (-)MapSizeX is replaced with -2 / 2 and
+ * 2 it added.
+ */
+static const AIRailSignalData _possible_trackdirs[5][NUM_TRACK_DIRECTIONS] = {
+ {{TRACK_UPPER, TRACKDIR_UPPER_E, 0}, {TRACK_Y, TRACKDIR_Y_SE, 0}, {TRACK_LEFT, TRACKDIR_LEFT_S, 1}},
+ {{TRACK_RIGHT, TRACKDIR_RIGHT_S, 1}, {TRACK_X, TRACKDIR_X_SW, 1}, {TRACK_UPPER, TRACKDIR_UPPER_W, 1}},
+ {{INVALID_TRACK, INVALID_TRACKDIR, 0}, {INVALID_TRACK, INVALID_TRACKDIR, 0}, {INVALID_TRACK, INVALID_TRACKDIR, 0}},
+ {{TRACK_LOWER, TRACKDIR_LOWER_E, 0}, {TRACK_X, TRACKDIR_X_NE, 0}, {TRACK_LEFT, TRACKDIR_LEFT_N, 0}},
+ {{TRACK_RIGHT, TRACKDIR_RIGHT_N, 0}, {TRACK_Y, TRACKDIR_Y_NW, 1}, {TRACK_LOWER, TRACKDIR_LOWER_W, 1}}
+};
+
+/* static */ AIRail::SignalType AIRail::GetSignalType(TileIndex tile, TileIndex front)
+{
+ if (AIMap::DistanceManhattan(tile, front) != 1) return SIGNALTYPE_NONE;
+ if (!::IsTileType(tile, MP_RAILWAY) || !::HasSignals(tile)) return SIGNALTYPE_NONE;
+
+ int data_index = 2 + (::TileX(front) - ::TileX(tile)) + 2 * (::TileY(front) - ::TileY(tile));
+
+ for (int i = 0; i < NUM_TRACK_DIRECTIONS; i++) {
+ const Track &track = _possible_trackdirs[data_index][i].track;
+ if (!(::TrackToTrackBits(track) & GetRailTracks(tile))) continue;
+ if (!HasSignalOnTrack(tile, track)) continue;
+ if (!HasSignalOnTrackdir(tile, _possible_trackdirs[data_index][i].trackdir)) continue;
+ SignalType st = (SignalType)::GetSignalType(tile, track);
+ if (HasSignalOnTrackdir(tile, ::ReverseTrackdir(_possible_trackdirs[data_index][i].trackdir))) st = (SignalType)(st | SIGNALTYPE_TWOWAY);
+ return st;
+ }
+
+ return SIGNALTYPE_NONE;
+}
+
+/**
+ * Check if signal_type is a valid SignalType.
+ */
+static bool IsValidSignalType(int signal_type)
+{
+ if (signal_type < AIRail::SIGNALTYPE_NORMAL || signal_type > AIRail::SIGNALTYPE_COMBO_TWOWAY) return false;
+ if (signal_type > AIRail::SIGNALTYPE_PBS_ONEWAY && signal_type < AIRail::SIGNALTYPE_NORMAL_TWOWAY) return false;
+ return true;
+}
+
+/* static */ bool AIRail::BuildSignal(TileIndex tile, TileIndex front, SignalType signal)
+{
+ EnforcePrecondition(false, AIMap::DistanceManhattan(tile, front) == 1)
+ EnforcePrecondition(false, ::IsTileType(tile, MP_RAILWAY) && ::IsPlainRailTile(tile));
+ EnforcePrecondition(false, ::IsValidSignalType(signal));
+
+ Track track = INVALID_TRACK;
+ uint signal_cycles;
+
+ int data_index = 2 + (::TileX(front) - ::TileX(tile)) + 2 * (::TileY(front) - ::TileY(tile));
+ for (int i = 0; i < NUM_TRACK_DIRECTIONS; i++) {
+ const Track &t = _possible_trackdirs[data_index][i].track;
+ if (!(::TrackToTrackBits(t) & GetRailTracks(tile))) continue;
+ track = t;
+ signal_cycles = _possible_trackdirs[data_index][i].signal_cycles;
+ break;
+ }
+ EnforcePrecondition(false, track != INVALID_TRACK);
+
+ uint p1 = track;
+ if (signal < SIGNALTYPE_TWOWAY) {
+ if (signal != SIGNALTYPE_PBS && signal != SIGNALTYPE_PBS_ONEWAY) signal_cycles++;
+ p1 |= (signal_cycles << 15);
+ }
+ p1 |= ((signal >= SIGNALTYPE_TWOWAY ? signal ^ SIGNALTYPE_TWOWAY : signal) << 5);
+
+ return AIObject::DoCommand(tile, p1, 0, CMD_BUILD_SIGNALS);
+}
+
+/* static */ bool AIRail::RemoveSignal(TileIndex tile, TileIndex front)
+{
+ EnforcePrecondition(false, AIMap::DistanceManhattan(tile, front) == 1)
+ EnforcePrecondition(false, GetSignalType(tile, front) != SIGNALTYPE_NONE);
+
+ Track track = INVALID_TRACK;
+ int data_index = 2 + (::TileX(front) - ::TileX(tile)) + 2 * (::TileY(front) - ::TileY(tile));
+ for (int i = 0; i < NUM_TRACK_DIRECTIONS; i++) {
+ const Track &t = _possible_trackdirs[data_index][i].track;
+ if (!(::TrackToTrackBits(t) & GetRailTracks(tile))) continue;
+ track = t;
+ break;
+ }
+ EnforcePrecondition(false, track != INVALID_TRACK);
+
+ return AIObject::DoCommand(tile, track, 0, CMD_REMOVE_SIGNALS);
+}
diff --git a/src/ai/api/ai_rail.hpp b/src/ai/api/ai_rail.hpp
new file mode 100644
index 000000000..5acb995ff
--- /dev/null
+++ b/src/ai/api/ai_rail.hpp
@@ -0,0 +1,390 @@
+/* $Id$ */
+
+/** @file ai_rail.hpp Everything to query and build rails. */
+
+#ifndef AI_RAIL_HPP
+#define AI_RAIL_HPP
+
+#include "ai_object.hpp"
+#include "ai_error.hpp"
+#include "ai_tile.hpp"
+
+/**
+ * Class that handles all rail related functions.
+ */
+class AIRail : public AIObject {
+public:
+ static const char *GetClassName() { return "AIRail"; }
+
+ /**
+ * All rail related error messages.
+ */
+ enum ErrorMessages {
+ /** Base for rail building / maintaining errors */
+ ERR_RAIL_BASE = AIError::ERR_CAT_RAIL << AIError::ERR_CAT_BIT_SIZE,
+
+ /** One-way roads cannot have crossings */
+ ERR_CROSSING_ON_ONEWAY_ROAD, // [STR_ERR_CROSSING_ON_ONEWAY_ROAD]
+
+ /** Track not suitable for signals */
+ ERR_UNSUITABLE_TRACK, // [STR_1005_NO_SUITABLE_RAILROAD_TRACK]
+
+ /** Non-uniform stations is diabled */
+ ERR_NONUNIFORM_STATIONS_DISABLED, // [STR_NONUNIFORM_STATIONS_DISALLOWED]
+ };
+
+ /**
+ * Types of rail known to the game.
+ */
+ enum RailType {
+ /* Note: the values _are_ important as they represent an in-game value */
+ RAILTYPE_INVALID = 0xFF, //!< Invalid RailType.
+ };
+
+ /**
+ * A bitmap with all possible rail tracks on a tile.
+ */
+ enum RailTrack {
+ /* Note: the values _are_ important as they represent an in-game value */
+ RAILTRACK_NE_SW = 1 << 0, //!< Track along the x-axis (north-east to south-west).
+ RAILTRACK_NW_SE = 1 << 1, //!< Track along the y-axis (north-west to south-east).
+ RAILTRACK_NW_NE = 1 << 2, //!< Track in the upper corner of the tile (north).
+ RAILTRACK_SW_SE = 1 << 3, //!< Track in the lower corner of the tile (south).
+ RAILTRACK_NW_SW = 1 << 4, //!< Track in the left corner of the tile (west).
+ RAILTRACK_NE_SE = 1 << 5, //!< Track in the right corner of the tile (east).
+ RAILTRACK_INVALID = 0xFF, //!< Flag for an invalid track.
+ };
+
+ /**
+ * Types of signal known to the game.
+ */
+ enum SignalType {
+ /* Note: the values _are_ important as they represent an in-game value */
+ SIGNALTYPE_NORMAL = 0, //!< Normal signal.
+ SIGNALTYPE_ENTRY = 1, //!< Entry presignal.
+ SIGNALTYPE_EXIT = 2, //!< Exit signal.
+ SIGNALTYPE_COMBO = 3, //!< Combo signal.
+ SIGNALTYPE_PBS = 4, //!< Normal PBS signal.
+ SIGNALTYPE_PBS_ONEWAY = 5, //!< No-entry PBS signal.
+ SIGNALTYPE_TWOWAY = 8, //!< Bit mask for twoway signal.
+ SIGNALTYPE_NORMAL_TWOWAY = SIGNALTYPE_NORMAL | SIGNALTYPE_TWOWAY, //!< Normal twoway signal.
+ SIGNALTYPE_ENTRY_TWOWAY = SIGNALTYPE_ENTRY | SIGNALTYPE_TWOWAY, //!< Entry twoway signal.
+ SIGNALTYPE_EXIT_TWOWAY = SIGNALTYPE_EXIT | SIGNALTYPE_TWOWAY, //!< Exit twoway signal.
+ SIGNALTYPE_COMBO_TWOWAY = SIGNALTYPE_COMBO | SIGNALTYPE_TWOWAY, //!< Combo twoway signal.
+ SIGNALTYPE_NONE = 0xFF, //!< No signal.
+ };
+
+ /**
+ * Checks whether the given tile is actually a tile with rail that can be
+ * used to traverse a tile. This excludes rail depots but includes
+ * stations and waypoints.
+ * @param tile The tile to check.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if and only if the tile has rail.
+ */
+ static bool IsRailTile(TileIndex tile);
+
+ /**
+ * Checks whether there is a road / rail crossing on a tile.
+ * @param tile The tile to check.
+ * @return True if and only if there is a road / rail crossing.
+ */
+ static bool IsLevelCrossingTile(TileIndex tile);
+
+ /**
+ * Checks whether the given tile is actually a tile with a rail depot.
+ * @param tile The tile to check.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if and only if the tile has a rail depot.
+ */
+ static bool IsRailDepotTile(TileIndex tile);
+
+ /**
+ * Checks whether the given tile is actually a tile with a rail station.
+ * @param tile The tile to check.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if and only if the tile has a rail station.
+ */
+ static bool IsRailStationTile(TileIndex tile);
+
+ /**
+ * Checks whether the given tile is actually a tile with a rail waypoint.
+ * @param tile The tile to check.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if and only if the tile has a rail waypoint.
+ */
+ static bool IsRailWaypointTile(TileIndex tile);
+
+ /**
+ * Check if a given RailType is available.
+ * @param rail_type The RailType to check for.
+ * @return True if this RailType can be used.
+ */
+ static bool IsRailTypeAvailable(RailType rail_type);
+
+ /**
+ * Get the current RailType set for all AIRail functions.
+ * @return The RailType currently set.
+ */
+ static RailType GetCurrentRailType();
+
+ /**
+ * Set the RailType for all further AIRail functions.
+ * @param rail_type The RailType to set.
+ */
+ static void SetCurrentRailType(RailType rail_type);
+
+ /**
+ * Check if a train build for a rail type can run on another rail type.
+ * @param engine_rail_type The rail type the train is build for.
+ * @param track_rail_type The type you want to check.
+ * @pre AIRail::IsRailTypeAvailable(engine_rail_type).
+ * @pre AIRail::IsRailTypeAvailable(track_rail_type).
+ * @return Whether a train build for 'engine_rail_type' 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 TrainHasPowerOnRail for that.
+ */
+ static bool TrainCanRunOnRail(AIRail::RailType engine_rail_type, AIRail::RailType track_rail_type);
+
+ /**
+ * Check if a train build for a rail type has power on another rail type.
+ * @param engine_rail_type The rail type the train is build for.
+ * @param track_rail_type The type you want to check.
+ * @pre AIRail::IsRailTypeAvailable(engine_rail_type).
+ * @pre AIRail::IsRailTypeAvailable(track_rail_type).
+ * @return Whether a train build for 'engine_rail_type' has power on 'track_rail_type'.
+ */
+ static bool TrainHasPowerOnRail(AIRail::RailType engine_rail_type, AIRail::RailType track_rail_type);
+
+ /**
+ * Get the RailType that is used on a tile.
+ * @param tile The tile to check.
+ * @pre AITile::HasTransportType(tile, AITile.TRANSPORT_RAIL).
+ * @return The RailType that is used on a tile.
+ */
+ static RailType GetRailType(TileIndex tile);
+
+ /**
+ * Convert the tracks on all tiles within a rectangle to another RailType.
+ * @param start_tile One corner of the rectangle.
+ * @param end_tile The opposite corner of the rectangle.
+ * @param convert_to The RailType you want to convert the rails to.
+ * @pre AIMap::IsValidTile(start_tile).
+ * @pre AIMap::IsValidTile(end_tile).
+ * @pre IsRailTypeAvailable(convert_to).
+ * @exception AIRail::ERR_UNSUITABLE_TRACK
+ * @return Whether at least some rail has been converted succesfully.
+ */
+ static bool ConvertRailType(TileIndex start_tile, TileIndex end_tile, AIRail::RailType convert_to);
+
+ /**
+ * Gets the tile in front of a rail depot.
+ * @param depot The rail depot tile.
+ * @pre IsRailDepotTile(depot).
+ * @return The tile in front of the depot.
+ */
+ static TileIndex GetRailDepotFrontTile(TileIndex depot);
+
+ /**
+ * Gets the direction of a rail station tile.
+ * @param tile The rail station tile.
+ * @pre IsRailStationTile(tile).
+ * @return The direction of the station (either RAILTRACK_NE_SW or RAILTRACK_NW_SE).
+ */
+ static RailTrack GetRailStationDirection(TileIndex tile);
+
+ /**
+ * Builds a rail depot.
+ * @param tile Place to build the depot.
+ * @param front The tile exactly in front of the depot.
+ * @pre AIMap::IsValidTile(tile).
+ * @pre AIMap::IsValidTile(front).
+ * @pre 'tile' is not equal to 'front', but in a straight line of it.
+ * @pre IsRailTypeAvailable(GetCurrentRailType()).
+ * @exception AIError::ERR_FLAT_LAND_REQUIRED
+ * @exception AIError::ERR_AREA_NOT_CLEAR
+ * @return Whether the rail depot has been/can be build or not.
+ */
+ static bool BuildRailDepot(TileIndex tile, TileIndex front);
+
+ /**
+ * Build a rail station.
+ * @param tile Place to build the station.
+ * @param direction The direction to build the station.
+ * @param num_platforms The number of platforms to build.
+ * @param platform_length The length of each platform.
+ * @param join_adjacent When building next to an other station, don't create a new station when this flag is true.
+ * @pre IsRailTypeAvailable(GetCurrentRailType()).
+ * @pre AIMap::IsValidTile(tile).
+ * @pre direction == RAILTRACK_NW_SE || direction == RAILTRACK_NE_SW.
+ * @pre num_platforms > 0 && num_platforms <= 255.
+ * @pre platform_length > 0 && platform_length <= 255.
+ * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
+ * @exception AIError::ERR_AREA_NOT_CLEAR
+ * @exception AIError::ERR_FLAT_LAND_REQUIRED
+ * @exception AIStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION
+ * @exception AIStation::ERR_STATION_TOO_MANY_STATIONS
+ * @exception AIStation::ERR_STATION_TOO_MANY_STATIONS_IN_TOWN
+ * @return Whether the station has been/can be build or not.
+ */
+ static bool BuildRailStation(TileIndex tile, RailTrack direction, uint num_platforms, uint platform_length, bool join_adjacent);
+
+ /**
+ * Build a NewGRF rail station. This calls callback 18 to let a NewGRF
+ * provide the station class / id to build, so we don't end up with
+ * only the default stations on the map.
+ * @param tile Place to build the station.
+ * @param direction The direction to build the station.
+ * @param num_platforms The number of platforms to build.
+ * @param platform_length The length of each platform.
+ * @param join_adjacent When building next to an other station, don't create a new station when this flag is true.
+ * @param cargo_id The CargoID of the cargo that will be transported from / to this station.
+ * @param source_industry The IndustryType of the industry you'll transport goods from.
+ * @param goal_industry The IndustryType of the industry you'll transport goods to.
+ * @param distance The manhattan distance you'll transport the cargo over.
+ * @param source_station True if this is the source station, false otherwise.
+ * @pre IsRailTypeAvailable(GetCurrentRailType()).
+ * @pre AIMap::IsValidTile(tile).
+ * @pre direction == RAILTRACK_NW_SE || direction == RAILTRACK_NE_SW.
+ * @pre num_platforms > 0 && num_platforms <= 255.
+ * @pre platform_length > 0 && platform_length <= 255.
+ * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
+ * @exception AIError::ERR_AREA_NOT_CLEAR
+ * @exception AIError::ERR_FLAT_LAND_REQUIRED
+ * @exception AIStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION
+ * @exception AIStation::ERR_STATION_TOO_MANY_STATIONS
+ * @exception AIStation::ERR_STATION_TOO_MANY_STATIONS_IN_TOWN
+ * @return Whether the station has been/can be build or not.
+ */
+ static bool BuildNewGRFRailStation(TileIndex tile, RailTrack direction, uint num_platforms, uint platform_length, bool join_adjacent, CargoID cargo_id, IndustryType source_industry, IndustryType goal_industry, int distance, bool source_station);
+
+ /**
+ * Remove a rectangle of platform pieces from a rail station.
+ * @param tile One corner of the rectangle to clear.
+ * @param tile2 The oppposite corner.
+ * @pre IsValidTile(tile).
+ * @pre IsValidTile(tile2).
+ * @return Whether at least one tile has been/can be cleared or not.
+ */
+ static bool RemoveRailStationTileRect(TileIndex tile, TileIndex tile2);
+
+ /**
+ * Get all RailTracks on the given tile.
+ * @param tile The tile to check.
+ * @pre IsRailTile(tile).
+ * @return A bitmask of RailTrack with all RailTracks on the tile.
+ */
+ static uint GetRailTracks(TileIndex tile);
+
+ /**
+ * Build rail on the given tile.
+ * @param tile The tile to build on.
+ * @param rail_track The RailTrack to build.
+ * @pre AIMap::IsValidTile(tile).
+ * @pre IsRailTypeAvailable(GetCurrentRailType()).
+ * @exception AIError::ERR_AREA_NOT_CLEAR
+ * @exception AIError::ERR_LAND_SLOPED_WRONG
+ * @exception AIRoad::ERR_ROAD_WORKS_IN_PROGRESS
+ * @exception AIRail::ERR_CROSSING_ON_ONEWAY_ROAD
+ * @exception AIError::ERR_ALREADY_BUILT
+ * @return Whether the rail has been/can be build or not.
+ * @note You can only build a single track with this function so do not
+ * use the values from RailTrack as bitmask.
+ */
+ static bool BuildRailTrack(TileIndex tile, RailTrack rail_track);
+
+ /**
+ * Remove rail on the given tile.
+ * @param tile The tile to remove rail from.
+ * @param rail_track The RailTrack to remove.
+ * @pre AIMap::IsValidTile(tile).
+ * @pre (GetRailTracks(tile) & rail_track) != 0.
+ * @return Whether the rail has been/can be removed or not.
+ * @note You can only remove a single track with this function so do not
+ * use the values from RailTrack as bitmask.
+ */
+ static bool RemoveRailTrack(TileIndex tile, RailTrack rail_track);
+
+ /**
+ * Check if a tile connects two adjacent tiles.
+ * @param from The first tile to connect.
+ * @param tile The tile that is checked.
+ * @param to The second tile to connect.
+ * @pre from != to.
+ * @pre AIMap::DistanceManhattan(from, tile) == 1.
+ * @pre AIMap::DistanceManhattan(to, tile) == 1.
+ * @return True if 'tile' connects 'from' and 'to'.
+ */
+ static bool AreTilesConnected(TileIndex from, TileIndex tile, TileIndex to);
+
+ /**
+ * Build a rail connection between two tiles.
+ * @param from The tile just before the tile to build on.
+ * @param tile The first tile to build on.
+ * @param to The tile just after the last tile to build on.
+ * @pre from != to.
+ * @pre AIMap::DistanceManhattan(from, tile) == 1.
+ * @pre AIMap::DistanceManhattan(to, tile) >= 1.
+ * @pre (abs(abs(AIMap::GetTileX(to) - AIMap::GetTileX(tile)) -
+ * abs(AIMap::GetTileY(to) - AIMap::GetTileY(tile))) <= 1) ||
+ * (AIMap::GetTileX(from) == AIMap::GetTileX(tile) && AIMap::GetTileX(tile) == AIMap::GetTileX(to)) ||
+ * (AIMap::GetTileY(from) == AIMap::GetTileY(tile) && AIMap::GetTileY(tile) == AIMap::GetTileY(to)).
+ * @pre IsRailTypeAvailable(GetCurrentRailType()).
+ * @exception AIError::ERR_AREA_NOT_CLEAR
+ * @exception AIError::ERR_LAND_SLOPED_WRONG
+ * @exception AIRail::ERR_CROSSING_ON_ONEWAY_ROAD
+ * @exception AIRoad::ERR_ROAD_WORKS_IN_PROGRESS
+ * @exception AIError::ERR_ALREADY_BUILT
+ * @return Whether the rail has been/can be build or not.
+ */
+ static bool BuildRail(TileIndex from, TileIndex tile, TileIndex to);
+
+ /**
+ * Remove a rail connection between two tiles.
+ * @param from The tile just before the tile to remove rail from.
+ * @param tile The first tile to remove rail from.
+ * @param to The tile just after the last tile to remove rail from.
+ * @pre from != to.
+ * @pre AIMap::DistanceManhattan(from, tile) == 1.
+ * @pre AIMap::DistanceManhattan(to, tile) >= 1.
+ * @pre (abs(abs(AIMap::GetTileX(to) - AIMap::GetTileX(tile)) -
+ * abs(AIMap::GetTileY(to) - AIMap::GetTileY(tile))) <= 1) ||
+ * (AIMap::GetTileX(from) == AIMap::GetTileX(tile) && AIMap::GetTileX(tile) == AIMap::GetTileX(to)) ||
+ * (AIMap::GetTileY(from) == AIMap::GetTileY(tile) && AIMap::GetTileY(tile) == AIMap::GetTileY(to)).
+ * @return Whether the rail has been/can be removed or not.
+ */
+ static bool RemoveRail(TileIndex from, TileIndex tile, TileIndex to);
+
+ /**
+ * Get the SignalType of the signal on a tile or SIGNALTYPE_NONE if there is no signal.
+ * @pre AIMap::DistanceManhattan(tile, front) == 1.
+ * @param tile The tile that might have a signal.
+ * @param front The tile in front of 'tile'.
+ * @return The SignalType of the signal on 'tile' with it's front to 'front'.
+ */
+ static SignalType GetSignalType(TileIndex tile, TileIndex front);
+
+ /**
+ * Build a signal on a tile.
+ * @param tile The tile to build on.
+ * @param front The tile in front of the signal.
+ * @param signal The SignalType to build.
+ * @pre AIMap::DistanceManhattan(tile, front) == 1.
+ * @pre IsRailTile(tile) && !IsRailStationTile(tile) && !IsRailWaypointTile(tile).
+ * @exception AIRail::ERR_UNSUITABLE_TRACK
+ * @return Whether the signal has been/can be build or not.
+ */
+ static bool BuildSignal(TileIndex tile, TileIndex front, SignalType signal);
+
+ /**
+ * Remove a signal.
+ * @param tile The tile to remove the signal from.
+ * @param front The tile in front of the signal.
+ * @pre AIMap::DistanceManhattan(tile, front) == 1.
+ * @pre GetSignalType(tile, front) != SIGNALTYPE_NONE.
+ * @return Whether the signal has been/can be removed or not.
+ */
+ static bool RemoveSignal(TileIndex tile, TileIndex front);
+};
+
+#endif /* AI_RAIL_HPP */
diff --git a/src/ai/api/ai_rail.hpp.sq b/src/ai/api/ai_rail.hpp.sq
new file mode 100644
index 000000000..a77d34c70
--- /dev/null
+++ b/src/ai/api/ai_rail.hpp.sq
@@ -0,0 +1,93 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_rail.hpp"
+
+namespace SQConvert {
+ /* Allow enums to be used as Squirrel parameters */
+ template <> AIRail::ErrorMessages GetParam(ForceType<AIRail::ErrorMessages>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIRail::ErrorMessages)tmp; }
+ template <> int Return<AIRail::ErrorMessages>(HSQUIRRELVM vm, AIRail::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; }
+ template <> AIRail::RailType GetParam(ForceType<AIRail::RailType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIRail::RailType)tmp; }
+ template <> int Return<AIRail::RailType>(HSQUIRRELVM vm, AIRail::RailType res) { sq_pushinteger(vm, (int32)res); return 1; }
+ template <> AIRail::RailTrack GetParam(ForceType<AIRail::RailTrack>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIRail::RailTrack)tmp; }
+ template <> int Return<AIRail::RailTrack>(HSQUIRRELVM vm, AIRail::RailTrack res) { sq_pushinteger(vm, (int32)res); return 1; }
+ template <> AIRail::SignalType GetParam(ForceType<AIRail::SignalType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIRail::SignalType)tmp; }
+ template <> int Return<AIRail::SignalType>(HSQUIRRELVM vm, AIRail::SignalType res) { sq_pushinteger(vm, (int32)res); return 1; }
+
+ /* Allow AIRail to be used as Squirrel parameter */
+ template <> AIRail *GetParam(ForceType<AIRail *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIRail *)instance; }
+ template <> AIRail &GetParam(ForceType<AIRail &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIRail *)instance; }
+ template <> const AIRail *GetParam(ForceType<const AIRail *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIRail *)instance; }
+ template <> const AIRail &GetParam(ForceType<const AIRail &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIRail *)instance; }
+ template <> int Return<AIRail *>(HSQUIRRELVM vm, AIRail *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIRail", res, NULL, DefSQDestructorCallback<AIRail>); return 1; }
+}; // namespace SQConvert
+
+void SQAIRail_Register(Squirrel *engine) {
+ DefSQClass <AIRail> SQAIRail("AIRail");
+ SQAIRail.PreRegister(engine);
+ SQAIRail.AddConstructor<void (AIRail::*)(), 1>(engine, "x");
+
+ SQAIRail.DefSQConst(engine, AIRail::ERR_RAIL_BASE, "ERR_RAIL_BASE");
+ SQAIRail.DefSQConst(engine, AIRail::ERR_CROSSING_ON_ONEWAY_ROAD, "ERR_CROSSING_ON_ONEWAY_ROAD");
+ SQAIRail.DefSQConst(engine, AIRail::ERR_UNSUITABLE_TRACK, "ERR_UNSUITABLE_TRACK");
+ SQAIRail.DefSQConst(engine, AIRail::ERR_NONUNIFORM_STATIONS_DISABLED, "ERR_NONUNIFORM_STATIONS_DISABLED");
+ SQAIRail.DefSQConst(engine, AIRail::RAILTYPE_INVALID, "RAILTYPE_INVALID");
+ SQAIRail.DefSQConst(engine, AIRail::RAILTRACK_NE_SW, "RAILTRACK_NE_SW");
+ SQAIRail.DefSQConst(engine, AIRail::RAILTRACK_NW_SE, "RAILTRACK_NW_SE");
+ SQAIRail.DefSQConst(engine, AIRail::RAILTRACK_NW_NE, "RAILTRACK_NW_NE");
+ SQAIRail.DefSQConst(engine, AIRail::RAILTRACK_SW_SE, "RAILTRACK_SW_SE");
+ SQAIRail.DefSQConst(engine, AIRail::RAILTRACK_NW_SW, "RAILTRACK_NW_SW");
+ SQAIRail.DefSQConst(engine, AIRail::RAILTRACK_NE_SE, "RAILTRACK_NE_SE");
+ SQAIRail.DefSQConst(engine, AIRail::RAILTRACK_INVALID, "RAILTRACK_INVALID");
+ SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_NORMAL, "SIGNALTYPE_NORMAL");
+ SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_ENTRY, "SIGNALTYPE_ENTRY");
+ SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_EXIT, "SIGNALTYPE_EXIT");
+ SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_COMBO, "SIGNALTYPE_COMBO");
+ SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_PBS, "SIGNALTYPE_PBS");
+ SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_PBS_ONEWAY, "SIGNALTYPE_PBS_ONEWAY");
+ SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_TWOWAY, "SIGNALTYPE_TWOWAY");
+ SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_NORMAL_TWOWAY, "SIGNALTYPE_NORMAL_TWOWAY");
+ SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_ENTRY_TWOWAY, "SIGNALTYPE_ENTRY_TWOWAY");
+ SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_EXIT_TWOWAY, "SIGNALTYPE_EXIT_TWOWAY");
+ SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_COMBO_TWOWAY, "SIGNALTYPE_COMBO_TWOWAY");
+ SQAIRail.DefSQConst(engine, AIRail::SIGNALTYPE_NONE, "SIGNALTYPE_NONE");
+
+ AIError::RegisterErrorMap(STR_ERR_CROSSING_ON_ONEWAY_ROAD, AIRail::ERR_CROSSING_ON_ONEWAY_ROAD);
+ AIError::RegisterErrorMap(STR_1005_NO_SUITABLE_RAILROAD_TRACK, AIRail::ERR_UNSUITABLE_TRACK);
+ AIError::RegisterErrorMap(STR_NONUNIFORM_STATIONS_DISALLOWED, AIRail::ERR_NONUNIFORM_STATIONS_DISABLED);
+
+ AIError::RegisterErrorMapString(AIRail::ERR_CROSSING_ON_ONEWAY_ROAD, "ERR_CROSSING_ON_ONEWAY_ROAD");
+ AIError::RegisterErrorMapString(AIRail::ERR_UNSUITABLE_TRACK, "ERR_UNSUITABLE_TRACK");
+ AIError::RegisterErrorMapString(AIRail::ERR_NONUNIFORM_STATIONS_DISABLED, "ERR_NONUNIFORM_STATIONS_DISABLED");
+
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::GetClassName, "GetClassName", 1, "x");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::IsRailTile, "IsRailTile", 2, "xi");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::IsLevelCrossingTile, "IsLevelCrossingTile", 2, "xi");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::IsRailDepotTile, "IsRailDepotTile", 2, "xi");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::IsRailStationTile, "IsRailStationTile", 2, "xi");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::IsRailWaypointTile, "IsRailWaypointTile", 2, "xi");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::IsRailTypeAvailable, "IsRailTypeAvailable", 2, "xi");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::GetCurrentRailType, "GetCurrentRailType", 1, "x");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::SetCurrentRailType, "SetCurrentRailType", 2, "xi");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::TrainCanRunOnRail, "TrainCanRunOnRail", 3, "xii");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::TrainHasPowerOnRail, "TrainHasPowerOnRail", 3, "xii");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::GetRailType, "GetRailType", 2, "xi");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::ConvertRailType, "ConvertRailType", 4, "xiii");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::GetRailDepotFrontTile, "GetRailDepotFrontTile", 2, "xi");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::GetRailStationDirection, "GetRailStationDirection", 2, "xi");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::BuildRailDepot, "BuildRailDepot", 3, "xii");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::BuildRailStation, "BuildRailStation", 6, "xiiiib");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::BuildNewGRFRailStation, "BuildNewGRFRailStation", 11, "xiiiibiiiib");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::RemoveRailStationTileRect, "RemoveRailStationTileRect", 3, "xii");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::GetRailTracks, "GetRailTracks", 2, "xi");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::BuildRailTrack, "BuildRailTrack", 3, "xii");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::RemoveRailTrack, "RemoveRailTrack", 3, "xii");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::AreTilesConnected, "AreTilesConnected", 4, "xiii");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::BuildRail, "BuildRail", 4, "xiii");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::RemoveRail, "RemoveRail", 4, "xiii");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::GetSignalType, "GetSignalType", 3, "xii");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::BuildSignal, "BuildSignal", 4, "xiii");
+ SQAIRail.DefSQStaticMethod(engine, &AIRail::RemoveSignal, "RemoveSignal", 3, "xii");
+
+ SQAIRail.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_railtypelist.cpp b/src/ai/api/ai_railtypelist.cpp
new file mode 100644
index 000000000..494cb5222
--- /dev/null
+++ b/src/ai/api/ai_railtypelist.cpp
@@ -0,0 +1,14 @@
+/* $Id$ */
+
+/** @file ai_railtypelist.cpp Implementation of AIRailTypeList and friends. */
+
+#include "ai_railtypelist.hpp"
+#include "../../rail.h"
+#include "../../company_func.h"
+
+AIRailTypeList::AIRailTypeList()
+{
+ for (RailType rt = RAILTYPE_BEGIN; rt != RAILTYPE_END; rt++) {
+ if (::HasRailtypeAvail(_current_company, rt)) this->AddItem(rt);
+ }
+}
diff --git a/src/ai/api/ai_railtypelist.hpp b/src/ai/api/ai_railtypelist.hpp
new file mode 100644
index 000000000..f9837d7b6
--- /dev/null
+++ b/src/ai/api/ai_railtypelist.hpp
@@ -0,0 +1,20 @@
+/* $Id$ */
+
+/** @file ai_railtypelist.hpp List all available railtypes. */
+
+#ifndef AI_RAILTYPELIST_HPP
+#define AI_RAILTYPELIST_HPP
+
+#include "ai_abstractlist.hpp"
+
+/**
+ * Creates a list of all available railtypes.
+ * @ingroup AIList
+ */
+class AIRailTypeList : public AIAbstractList {
+public:
+ static const char *GetClassName() { return "AIRailTypeList"; }
+ AIRailTypeList();
+};
+
+#endif /* AI_RAILTYPELIST_HPP */
diff --git a/src/ai/api/ai_railtypelist.hpp.sq b/src/ai/api/ai_railtypelist.hpp.sq
new file mode 100644
index 000000000..255124bf6
--- /dev/null
+++ b/src/ai/api/ai_railtypelist.hpp.sq
@@ -0,0 +1,23 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_railtypelist.hpp"
+
+namespace SQConvert {
+ /* Allow AIRailTypeList to be used as Squirrel parameter */
+ template <> AIRailTypeList *GetParam(ForceType<AIRailTypeList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIRailTypeList *)instance; }
+ template <> AIRailTypeList &GetParam(ForceType<AIRailTypeList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIRailTypeList *)instance; }
+ template <> const AIRailTypeList *GetParam(ForceType<const AIRailTypeList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIRailTypeList *)instance; }
+ template <> const AIRailTypeList &GetParam(ForceType<const AIRailTypeList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIRailTypeList *)instance; }
+ template <> int Return<AIRailTypeList *>(HSQUIRRELVM vm, AIRailTypeList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIRailTypeList", res, NULL, DefSQDestructorCallback<AIRailTypeList>); return 1; }
+}; // namespace SQConvert
+
+void SQAIRailTypeList_Register(Squirrel *engine) {
+ DefSQClass <AIRailTypeList> SQAIRailTypeList("AIRailTypeList");
+ SQAIRailTypeList.PreRegister(engine, "AIAbstractList");
+ SQAIRailTypeList.AddConstructor<void (AIRailTypeList::*)(), 1>(engine, "x");
+
+ SQAIRailTypeList.DefSQStaticMethod(engine, &AIRailTypeList::GetClassName, "GetClassName", 1, "x");
+
+ SQAIRailTypeList.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_road.cpp b/src/ai/api/ai_road.cpp
new file mode 100644
index 000000000..a6a14656c
--- /dev/null
+++ b/src/ai/api/ai_road.cpp
@@ -0,0 +1,548 @@
+/* $Id$ */
+
+/** @file ai_road.cpp Implementation of AIRoad. */
+
+#include "ai_road.hpp"
+#include "ai_map.hpp"
+#include "ai_list.hpp"
+#include "../../openttd.h"
+#include "../../road_map.h"
+#include "../../station_map.h"
+#include "../../tunnelbridge_map.h"
+#include "../../command_type.h"
+#include "../../company_func.h"
+#include "../../settings_type.h"
+#include "../../script/squirrel_helper_type.hpp"
+
+/* static */ bool AIRoad::IsRoadTile(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return false;
+
+ return (::IsTileType(tile, MP_ROAD) && ::GetRoadTileType(tile) != ROAD_TILE_DEPOT) ||
+ IsDriveThroughRoadStationTile(tile);
+}
+
+/* static */ bool AIRoad::IsRoadDepotTile(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return false;
+
+ return ::IsTileType(tile, MP_ROAD) && ::GetRoadTileType(tile) == ROAD_TILE_DEPOT &&
+ (::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType()) & ::GetRoadTypes(tile)) != 0;
+}
+
+/* static */ bool AIRoad::IsRoadStationTile(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return false;
+
+ return ::IsRoadStopTile(tile) && (::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType()) & ::GetRoadTypes(tile)) != 0;
+}
+
+/* static */ bool AIRoad::IsDriveThroughRoadStationTile(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return false;
+
+ return ::IsDriveThroughStopTile(tile) && (::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType()) & ::GetRoadTypes(tile)) != 0;
+}
+
+/* static */ bool AIRoad::IsRoadTypeAvailable(RoadType road_type)
+{
+ return ::HasRoadTypesAvail(_current_company, ::RoadTypeToRoadTypes((::RoadType)road_type));
+}
+
+/* static */ AIRoad::RoadType AIRoad::GetCurrentRoadType()
+{
+ return (RoadType)AIObject::GetRoadType();
+}
+
+/* static */ void AIRoad::SetCurrentRoadType(RoadType road_type)
+{
+ if (!IsRoadTypeAvailable(road_type)) return;
+
+ AIObject::SetRoadType((::RoadType)road_type);
+}
+
+/* static */ bool AIRoad::HasRoadType(TileIndex tile, RoadType road_type)
+{
+ if (!AIMap::IsValidTile(tile)) return false;
+ if (!IsRoadTypeAvailable(road_type)) return false;
+ return ::GetAnyRoadBits(tile, (::RoadType)road_type, false) != ROAD_NONE;
+}
+
+/* static */ bool AIRoad::AreRoadTilesConnected(TileIndex t1, TileIndex t2)
+{
+ if (!::IsValidTile(t1)) return false;
+ if (!::IsValidTile(t2)) return false;
+
+ /* Tiles not neighbouring */
+ if ((abs((int)::TileX(t1) - (int)::TileX(t2)) + abs((int)::TileY(t1) - (int)::TileY(t2))) != 1) return false;
+
+ RoadBits r1 = ::GetAnyRoadBits(t1, AIObject::GetRoadType());
+ RoadBits r2 = ::GetAnyRoadBits(t2, AIObject::GetRoadType());
+
+ uint dir_1 = (::TileX(t1) == ::TileX(t2)) ? (::TileY(t1) < ::TileY(t2) ? 2 : 0) : (::TileX(t1) < ::TileX(t2) ? 1 : 3);
+ uint dir_2 = 2 ^ dir_1;
+
+ DisallowedRoadDirections drd2 = IsNormalRoadTile(t2) ? GetDisallowedRoadDirections(t2) : DRD_NONE;
+
+ return HasBit(r1, dir_1) && HasBit(r2, dir_2) && drd2 != DRD_BOTH && drd2 != (dir_1 > dir_2 ? DRD_SOUTHBOUND : DRD_NORTHBOUND);
+}
+
+/* Helper functions for AIRoad::CanBuildConnectedRoadParts(). */
+
+/**
+ * Check whether the given existing bits the start and end part can be build.
+ * As the function assumes the bits being build on a slope that does not
+ * allow level foundations all of the existing parts will always be in
+ * a straight line. This also needs to hold for the start and end parts,
+ * otherwise it is for sure not valid. Finally a check will be done to
+ * determine whether the existing road parts match the to-be-build parts.
+ * As they can only be placed in one direction, just checking the start
+ * part with the first existing part is enough.
+ * @param existing The existing road parts.
+ * @param start The part that should be build first.
+ * @param end The part that will be build second.
+ * @return True if and only if the road bits can be build.
+ */
+static bool CheckAutoExpandedRoadBits(const Array *existing, int32 start, int32 end)
+{
+ return (start + end == 0) && (existing->size == 0 || existing->array[0] == start || existing->array[0] == end);
+}
+
+/**
+ * Lookup function for building road parts when building on slopes is disabled.
+ * @param slope The slope of the tile to examine.
+ * @param existing The existing road parts.
+ * @param start The part that should be build first.
+ * @param end The part that will be build second.
+ * @return 0 when the build parts do not connect, 1 when they do connect once
+ * they are build or 2 when building the first part automatically
+ * builds the second part.
+ */
+static int32 LookupWithoutBuildOnSlopes(::Slope slope, const Array *existing, int32 start, int32 end)
+{
+ switch (slope) {
+ /* Flat slopes can always be build. */
+ case SLOPE_FLAT:
+ return 1;
+
+ /* Only 4 of the slopes can be build upon. Testing the existing bits is
+ * necessary because these bits can be something else when the settings
+ * in the game have been changed.
+ */
+ case SLOPE_NE: case SLOPE_SW:
+ return (CheckAutoExpandedRoadBits(existing, start, end) && (start == 1 || end == 1)) ? (existing->size == 0 ? 2 : 1) : 0;
+ case SLOPE_SE: case SLOPE_NW:
+ return (CheckAutoExpandedRoadBits(existing, start, end) && (start != 1 && end != 1)) ? (existing->size == 0 ? 2 : 1) : 0;
+
+ /* Any other tile cannot be built on. */
+ default:
+ return 0;
+ }
+}
+
+/**
+ * Rotate a neighbour bit a single time clockwise.
+ * @param neighbour The neighbour.
+ * @return The rotate neighbour data.
+ */
+static int32 RotateNeighbour(int32 neighbour)
+{
+ switch (neighbour) {
+ case -2: return -1;
+ case -1: return 2;
+ case 1: return -2;
+ case 2: return 1;
+ default: NOT_REACHED();
+ }
+}
+
+/**
+ * Convert a neighbour to a road bit representation for easy internal use.
+ * @param neighbour The neighbour.
+ * @return The bits representing the direction.
+ */
+static RoadBits NeighbourToRoadBits(int32 neighbour)
+{
+ switch (neighbour) {
+ case -2: return ROAD_NW;
+ case -1: return ROAD_NE;
+ case 2: return ROAD_SE;
+ case 1: return ROAD_SW;
+ default: NOT_REACHED();
+ }
+}
+
+/**
+ * Lookup function for building road parts when building on slopes is enabled.
+ * @param slope The slope of the tile to examine.
+ * @param existing The existing neighbours.
+ * @param start The part that should be build first.
+ * @param end The part that will be build second.
+ * @return 0 when the build parts do not connect, 1 when they do connect once
+ * they are build or 2 when building the first part automatically
+ * builds the second part.
+ */
+static int32 LookupWithBuildOnSlopes(::Slope slope, Array *existing, int32 start, int32 end)
+{
+ if (::IsSteepSlope(slope)) {
+ switch (slope) {
+ /* On steep slopes one can only build straight roads that will be
+ * automatically expanded to a straight road. Just check that the existing
+ * road parts are in the same direction. */
+ case SLOPE_STEEP_S:
+ case SLOPE_STEEP_W:
+ case SLOPE_STEEP_N:
+ case SLOPE_STEEP_E:
+ return CheckAutoExpandedRoadBits(existing, start, end) ? (existing->size == 0 ? 2 : 1) : 0;
+
+ /* All other slopes are invalid slopes!. */
+ default:
+ return -1;
+ }
+ }
+
+ /* The slope is not steep. Furthermore lots of slopes are generally the
+ * same but are only rotated. So to reduce the amount of lookup work that
+ * needs to be done the data is made uniform. This means rotating the
+ * existing parts and updating the slope. */
+ static const ::Slope base_slopes[] = {
+ SLOPE_FLAT, SLOPE_W, SLOPE_W, SLOPE_SW,
+ SLOPE_W, SLOPE_EW, SLOPE_SW, SLOPE_WSE,
+ SLOPE_W, SLOPE_SW, SLOPE_EW, SLOPE_WSE,
+ SLOPE_SW, SLOPE_WSE, SLOPE_WSE};
+ static const byte base_rotates[] = {0, 0, 1, 0, 2, 0, 1, 0, 3, 3, 2, 3, 2, 2, 1};
+
+ if (slope >= (::Slope)lengthof(base_slopes)) {
+ /* This slope is an invalid slope, so ignore it. */
+ return -1;
+ }
+ byte base_rotate = base_rotates[slope];
+ slope = base_slopes[slope];
+
+ /* Some slopes don't need rotating, so return early when we know we do
+ * not need to rotate. */
+ switch (slope) {
+ case SLOPE_FLAT:
+ /* Flat slopes can always be build. */
+ return 1;
+
+ case SLOPE_EW:
+ case SLOPE_WSE:
+ /* A slope similar to a SLOPE_EW or SLOPE_WSE will always cause
+ * foundations which makes them accessible from all sides. */
+ return 1;
+
+ case SLOPE_W:
+ case SLOPE_SW:
+ /* A slope for which we need perform some calculations. */
+ break;
+
+ default:
+ /* An invalid slope. */
+ return -1;
+ }
+
+ /* Now perform the actual rotation. */
+ for (int j = 0; j < base_rotate; j++) {
+ for (int i = 0; i < existing->size; i++) {
+ existing->array[i] = RotateNeighbour(existing->array[i]);
+ }
+ start = RotateNeighbour(start);
+ end = RotateNeighbour(end);
+ }
+
+ /* Create roadbits out of the data for easier handling. */
+ RoadBits start_roadbits = NeighbourToRoadBits(start);
+ RoadBits new_roadbits = start_roadbits | NeighbourToRoadBits(end);
+ RoadBits existing_roadbits = ROAD_NONE;
+ for (int i = 0; i < existing->size; i++) {
+ existing_roadbits |= NeighbourToRoadBits(existing->array[i]);
+ }
+
+ switch (slope) {
+ case SLOPE_W:
+ /* A slope similar to a SLOPE_W. */
+ switch (new_roadbits) {
+ case 6: // ROAD_SE | ROAD_SW:
+ case 9: // ROAD_NE | ROAD_NW:
+ case 12: // ROAD_NE | ROAD_SE:
+ /* Cannot build anything with a turn from the low side. */
+ return 0;
+
+ case 5: // ROAD_SE | ROAD_NW:
+ case 10: // ROAD_NE | ROAD_SW:
+ /* A 'sloped' tile is going to be build. */
+ if ((existing_roadbits | new_roadbits) != new_roadbits) {
+ /* There is already a foundation on the tile, or at least
+ * another slope that is not compatible with the new one. */
+ return 0;
+ }
+ /* If the start is in the low part, it is automatically
+ * building the second part too. */
+ return ((start_roadbits & (ROAD_NE | ROAD_SE)) && !(existing_roadbits & (ROAD_SW | ROAD_NW))) ? 2 : 1;
+
+ default:
+ /* Roadbits causing a foundation are going to be build.
+ * When the existing roadbits are slopes (the lower bits
+ * are used), this cannot be done. */
+ if ((existing_roadbits | new_roadbits) == new_roadbits) return 1;
+ return (existing_roadbits & (ROAD_NE | ROAD_SE)) ? 0 : 1;
+ }
+
+ case SLOPE_SW:
+ /* A slope similar to a SLOPE_SW. */
+ switch (new_roadbits) {
+ case 9: // ROAD_NE | ROAD_NW:
+ case 12: // ROAD_NE | ROAD_SE:
+ /* Cannot build anything with a turn from the low side. */
+ return 0;
+
+ case 10: // ROAD_NE | ROAD_SW:
+ /* A 'sloped' tile is going to be build. */
+ if ((existing_roadbits | new_roadbits) != new_roadbits) {
+ /* There is already a foundation on the tile, or at least
+ * another slope that is not compatible with the new one. */
+ return 0;
+ }
+ /* If the start is in the low part, it is automatically
+ * building the second part too. */
+ return ((start_roadbits & ROAD_NE) && !(existing_roadbits & ROAD_SW)) ? 2 : 1;
+
+ default:
+ /* Roadbits causing a foundation are going to be build.
+ * When the existing roadbits are slopes (the lower bits
+ * are used), this cannot be done. */
+ return (existing_roadbits & ROAD_NE) ? 0 : 1;
+ }
+
+ default:
+ NOT_REACHED();
+ }
+}
+
+/**
+ * Normalise all input data so we can easily handle it without needing
+ * to call the API lots of times or create large if-elseif-elseif-else
+ * constructs.
+ * In this case it means that a TileXY(0, -1) becomes -2 and TileXY(0, 1)
+ * becomes 2. TileXY(-1, 0) and TileXY(1, 0) stay respectively -1 and 1.
+ * Any other value means that it is an invalid tile offset.
+ * @param tile The tile to normalise.
+ * @return True if and only if the tile offset is valid.
+ */
+static bool NormaliseTileOffset(int32 *tile)
+{
+ if (*tile == 1 || *tile == -1) return true;
+ if (*tile == ::TileDiffXY(0, -1)) {
+ *tile = -2;
+ return true;
+ }
+ if (*tile == ::TileDiffXY(0, 1)) {
+ *tile = 2;
+ return true;
+ }
+ return false;
+}
+
+/* static */ int32 AIRoad::CanBuildConnectedRoadParts(AITile::Slope slope_, Array *existing, TileIndex start_, TileIndex end_)
+{
+ ::Slope slope = (::Slope)slope_;
+ int32 start = start_;
+ int32 end = end_;
+
+ /* The start tile and end tile cannot be the same tile either. */
+ if (start == end) return -1;
+
+ for (int i = 0; i < existing->size; i++) {
+ if (!NormaliseTileOffset(&existing->array[i])) return -1;
+ }
+
+ if (!NormaliseTileOffset(&start)) return -1;
+ if (!NormaliseTileOffset(&end)) return -1;
+
+ /* Without build on slopes the characteristics are vastly different, so use
+ * a different helper function (one that is much simpler). */
+ return _settings_game.construction.build_on_slopes ? LookupWithBuildOnSlopes(slope, existing, start, end) : LookupWithoutBuildOnSlopes(slope, existing, start, end);
+}
+
+/* static */ int32 AIRoad::CanBuildConnectedRoadPartsHere(TileIndex tile, TileIndex start, TileIndex end)
+{
+ if (!::IsValidTile(tile) || !::IsValidTile(start) || !::IsValidTile(end)) return -1;
+ if (::DistanceManhattan(tile, start) != 1 || ::DistanceManhattan(tile, end) != 1) return -1;
+
+ /* ROAD_NW ROAD_SW ROAD_SE ROAD_NE */
+ static const TileIndex neighbours[] = {::TileDiffXY(0, -1), ::TileDiffXY(1, 0), ::TileDiffXY(0, 1), ::TileDiffXY(-1, 0)};
+ Array *existing = (Array*)alloca(sizeof(Array) + lengthof(neighbours) * sizeof(int32));
+ existing->size = 0;
+
+ ::RoadBits rb = ::ROAD_NONE;
+ if (::IsNormalRoadTile(tile)) {
+ rb = ::GetAllRoadBits(tile);
+ } else {
+ for (::RoadType rt = ::ROADTYPE_BEGIN; rt < ::ROADTYPE_END; rt++) rb |= ::GetAnyRoadBits(tile, rt);
+ }
+ for (uint i = 0; i < lengthof(neighbours); i++) {
+ if (HasBit(rb, i)) existing->array[existing->size++] = neighbours[i];
+ }
+
+ return AIRoad::CanBuildConnectedRoadParts(AITile::GetSlope(tile), existing, start - tile, end - tile);
+}
+
+/**
+ * Check whether one can reach (possibly by building) a road piece the center
+ * of the neighbouring tile. This includes roads and (drive through) stations.
+ * @param start_tile The tile to "enter" the neighbouring tile.
+ * @param neighbour The direction to the neighbouring tile to "enter".
+ * @return true if and only if the tile is reachable.
+ */
+static bool NeighbourHasReachableRoad(::RoadTypes rts, TileIndex start_tile, DiagDirection neighbour)
+{
+ TileIndex neighbour_tile = ::TileAddByDiagDir(start_tile, neighbour);
+ if ((rts & ::GetRoadTypes(neighbour_tile)) == 0) return false;
+
+ switch (::GetTileType(neighbour_tile)) {
+ case MP_ROAD:
+ return (::GetRoadTileType(neighbour_tile) != ROAD_TILE_DEPOT);
+
+ case MP_STATION:
+ if (::IsDriveThroughStopTile(neighbour_tile)) {
+ return (::DiagDirToAxis(neighbour) == ::DiagDirToAxis(::GetRoadStopDir(neighbour_tile)));
+ }
+ return false;
+
+ default:
+ return false;
+ }
+}
+
+/* static */ int32 AIRoad::GetNeighbourRoadCount(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return false;
+
+ ::RoadTypes rts = ::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType());
+ int32 neighbour = 0;
+
+ if (NeighbourHasReachableRoad(rts, tile, DIAGDIR_NE)) neighbour++;
+ if (NeighbourHasReachableRoad(rts, tile, DIAGDIR_SE)) neighbour++;
+ if (NeighbourHasReachableRoad(rts, tile, DIAGDIR_SW)) neighbour++;
+ if (NeighbourHasReachableRoad(rts, tile, DIAGDIR_NW)) neighbour++;
+
+ return neighbour;
+}
+
+/* static */ TileIndex AIRoad::GetRoadDepotFrontTile(TileIndex depot)
+{
+ if (!IsRoadDepotTile(depot)) return INVALID_TILE;
+
+ return depot + ::TileOffsByDiagDir(::GetRoadDepotDirection(depot));
+}
+
+/* static */ TileIndex AIRoad::GetRoadStationFrontTile(TileIndex station)
+{
+ if (!IsRoadStationTile(station)) return INVALID_TILE;
+
+ return station + ::TileOffsByDiagDir(::GetRoadStopDir(station));
+}
+
+/* static */ TileIndex AIRoad::GetDriveThroughBackTile(TileIndex station)
+{
+ if (!IsDriveThroughRoadStationTile(station)) return INVALID_TILE;
+
+ return station + ::TileOffsByDiagDir(::ReverseDiagDir(::GetRoadStopDir(station)));
+}
+
+/* static */ bool AIRoad::_BuildRoadInternal(TileIndex start, TileIndex end, bool one_way, bool full)
+{
+ EnforcePrecondition(false, start != end);
+ EnforcePrecondition(false, ::IsValidTile(start));
+ EnforcePrecondition(false, ::IsValidTile(end));
+ EnforcePrecondition(false, ::TileX(start) == ::TileX(end) || ::TileY(start) == ::TileY(end));
+ EnforcePrecondition(false, !one_way || AIObject::GetRoadType() == ::ROADTYPE_ROAD);
+
+ return AIObject::DoCommand(end, start, (::TileY(start) != ::TileY(end) ? 4 : 0) | (((start < end) == !full) ? 1 : 2) | (AIObject::GetRoadType() << 3) | ((one_way ? 1 : 0) << 5), CMD_BUILD_LONG_ROAD);
+}
+
+/* static */ bool AIRoad::BuildRoad(TileIndex start, TileIndex end)
+{
+ return _BuildRoadInternal(start, end, false, false);
+}
+
+/* static */ bool AIRoad::BuildOneWayRoad(TileIndex start, TileIndex end)
+{
+ return _BuildRoadInternal(start, end, true, false);
+}
+
+/* static */ bool AIRoad::BuildRoadFull(TileIndex start, TileIndex end)
+{
+ return _BuildRoadInternal(start, end, false, true);
+}
+
+/* static */ bool AIRoad::BuildOneWayRoadFull(TileIndex start, TileIndex end)
+{
+ return _BuildRoadInternal(start, end, true, true);
+}
+
+/* static */ bool AIRoad::BuildRoadDepot(TileIndex tile, TileIndex front)
+{
+ EnforcePrecondition(false, tile != front);
+ EnforcePrecondition(false, ::IsValidTile(tile));
+ EnforcePrecondition(false, ::IsValidTile(front));
+ EnforcePrecondition(false, ::TileX(tile) == ::TileX(front) || ::TileY(tile) == ::TileY(front));
+
+ uint entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? 1 : 3) : (::TileX(tile) < ::TileX(front) ? 2 : 0);
+
+ return AIObject::DoCommand(tile, entrance_dir | (AIObject::GetRoadType() << 2), 0, CMD_BUILD_ROAD_DEPOT);
+}
+
+/* static */ bool AIRoad::BuildRoadStation(TileIndex tile, TileIndex front, bool truck, bool drive_through, bool join_adjacent)
+{
+ EnforcePrecondition(false, tile != front);
+ EnforcePrecondition(false, ::IsValidTile(tile));
+ EnforcePrecondition(false, ::IsValidTile(front));
+ EnforcePrecondition(false, ::TileX(tile) == ::TileX(front) || ::TileY(tile) == ::TileY(front));
+
+ uint entrance_dir;
+ if (drive_through) {
+ entrance_dir = ::TileY(tile) != ::TileY(front);
+ } else {
+ entrance_dir = (::TileX(tile) == ::TileX(front)) ? (::TileY(tile) < ::TileY(front) ? 1 : 3) : (::TileX(tile) < ::TileX(front) ? 2 : 0);
+ }
+
+ return AIObject::DoCommand(tile, entrance_dir, (join_adjacent ? 0 : 32) | (drive_through ? 2 : 0) | (truck ? 1 : 0) | (::RoadTypeToRoadTypes(AIObject::GetRoadType()) << 2) | (INVALID_STATION << 16), CMD_BUILD_ROAD_STOP);
+}
+
+/* static */ bool AIRoad::RemoveRoad(TileIndex start, TileIndex end)
+{
+ EnforcePrecondition(false, ::IsValidTile(start));
+ EnforcePrecondition(false, ::IsValidTile(end));
+ EnforcePrecondition(false, ::TileX(start) == ::TileX(end) || ::TileY(start) == ::TileY(end));
+
+ return AIObject::DoCommand(end, start, (::TileY(start) != ::TileY(end) ? 4 : 0) | (start < end ? 1 : 2) | (AIObject::GetRoadType() << 3), CMD_REMOVE_LONG_ROAD);
+}
+
+/* static */ bool AIRoad::RemoveRoadFull(TileIndex start, TileIndex end)
+{
+ EnforcePrecondition(false, ::IsValidTile(start));
+ EnforcePrecondition(false, ::IsValidTile(end));
+ EnforcePrecondition(false, ::TileX(start) == ::TileX(end) || ::TileY(start) == ::TileY(end));
+
+ return AIObject::DoCommand(end, start, (::TileY(start) != ::TileY(end) ? 4 : 0) | (start < end ? 2 : 1) | (AIObject::GetRoadType() << 3), CMD_REMOVE_LONG_ROAD);
+}
+
+/* static */ bool AIRoad::RemoveRoadDepot(TileIndex tile)
+{
+ EnforcePrecondition(false, ::IsValidTile(tile));
+ EnforcePrecondition(false, IsTileType(tile, MP_ROAD))
+ EnforcePrecondition(false, GetRoadTileType(tile) == ROAD_TILE_DEPOT);
+
+ return AIObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR);
+}
+
+/* static */ bool AIRoad::RemoveRoadStation(TileIndex tile)
+{
+ EnforcePrecondition(false, ::IsValidTile(tile));
+ EnforcePrecondition(false, IsTileType(tile, MP_STATION));
+ EnforcePrecondition(false, IsRoadStop(tile));
+
+ return AIObject::DoCommand(tile, 0, GetRoadStopType(tile), CMD_REMOVE_ROAD_STOP);
+}
diff --git a/src/ai/api/ai_road.hpp b/src/ai/api/ai_road.hpp
new file mode 100644
index 000000000..c80f63177
--- /dev/null
+++ b/src/ai/api/ai_road.hpp
@@ -0,0 +1,408 @@
+/* $Id$ */
+
+/** @file ai_road.hpp Everything to query and build roads. */
+
+#ifndef AI_ROAD_HPP
+#define AI_ROAD_HPP
+
+#include "ai_object.hpp"
+#include "ai_error.hpp"
+#include "ai_tile.hpp"
+
+/**
+ * Class that handles all road related functions.
+ */
+class AIRoad : public AIObject {
+public:
+ static const char *GetClassName() { return "AIRoad"; }
+
+ /**
+ * All road related error messages.
+ */
+ enum ErrorMessages {
+ /** Base for road building / maintaining errors */
+ ERR_ROAD_BASE = AIError::ERR_CAT_ROAD << AIError::ERR_CAT_BIT_SIZE,
+
+ /** Road works are in progress */
+ ERR_ROAD_WORKS_IN_PROGRESS, // [STR_ROAD_WORKS_IN_PROGRESS]
+
+ /** Drive through is in the wrong direction */
+ ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION, // [STR_DRIVE_THROUGH_ERROR_DIRECTION]
+
+ /** Drive through roads can't be build on town owned roads */
+ ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD, // [STR_DRIVE_THROUGH_ERROR_ON_TOWN_ROAD]
+
+
+ /** One way roads can't have junctions */
+ ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS, // [STR_ERR_ONEWAY_ROADS_CAN_T_HAVE_JUNCTION]
+ };
+
+ /**
+ * Types of road known to the game.
+ */
+ enum RoadType {
+ /* Values are important, as they represent the internal state of the game. */
+ ROADTYPE_ROAD = 0, //!< Build road objects.
+ ROADTYPE_TRAM = 1, //!< Build tram objects.
+
+ ROADTYPE_INVALID = -1, //!< Invalid RoadType.
+ };
+
+ /**
+ * Checks whether the given tile is actually a tile with road that can be
+ * used to traverse a tile. This excludes road depots and 'normal' road
+ * stations, but includes drive through stations.
+ * @param tile The tile to check.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if and only if the tile has road.
+ */
+ static bool IsRoadTile(TileIndex tile);
+
+ /**
+ * Checks whether the given tile is actually a tile with a road depot.
+ * @param tile The tile to check.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if and only if the tile has a road depot.
+ */
+ static bool IsRoadDepotTile(TileIndex tile);
+
+ /**
+ * Checks whether the given tile is actually a tile with a road station.
+ * @param tile The tile to check.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if and only if the tile has a road station.
+ */
+ static bool IsRoadStationTile(TileIndex tile);
+
+ /**
+ * Checks whether the given tile is actually a tile with a drive through
+ * road station.
+ * @param tile The tile to check.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if and only if the tile has a drive through road station.
+ */
+ static bool IsDriveThroughRoadStationTile(TileIndex tile);
+
+ /**
+ * Check if a given RoadType is available.
+ * @param road_type The RoadType to check for.
+ * @return True if this RoadType can be used.
+ */
+ static bool IsRoadTypeAvailable(RoadType road_type);
+
+ /**
+ * Get the current RoadType set for all AIRoad functions.
+ * @return The RoadType currently set.
+ */
+ static RoadType GetCurrentRoadType();
+
+ /**
+ * Set the RoadType for all further AIRoad functions.
+ * @param road_type The RoadType to set.
+ */
+ static void SetCurrentRoadType(RoadType road_type);
+
+ /**
+ * Check if a given tile has RoadType.
+ * @param tile The tile to check.
+ * @param road_type The RoadType to check for.
+ * @pre AIMap::IsValidTile(tile).
+ * @pre IsRoadTypeAvailable(road_type).
+ * @return True if the tile contains a RoadType object.
+ */
+ static bool HasRoadType(TileIndex tile, RoadType road_type);
+
+ /**
+ * Checks whether the given tiles are directly connected, i.e. whether
+ * a road vehicle can travel from the center of the first tile to the
+ * center of the second tile.
+ * @param tile_from The source tile.
+ * @param tile_to The destination tile.
+ * @pre AIMap::IsValidTile(tile_from).
+ * @pre AIMap::IsValidTile(tile_to).
+ * @pre 'tile_from' and 'tile_to' are directly neighbouring tiles.
+ * @return True if and only if a road vehicle can go from tile_from to tile_to.
+ */
+ static bool AreRoadTilesConnected(TileIndex tile_from, TileIndex tile_to);
+
+ /**
+ * Lookup function for building road parts independend on whether the
+ * "building on slopes" setting is enabled or not.
+ * This implementation can be used for abstract reasoning about a tile as
+ * it needs the slope and existing road parts of the tile as information.
+ * @param slope The slope of the tile to examine.
+ * @param existing An array with the existing neighbours in the same format
+ * as "start" and "end", e.g. AIMap.GetTileIndex(0, 1).
+ * As a result of this all values of the existing array
+ * must be of type integer.
+ * @param start The tile from where the 'tile to be considered' will be
+ * entered. This is a relative tile, so valid parameters are:
+ * AIMap.GetTileIndex(0, 1), AIMap.GetTileIndex(0, -1),
+ * AIMap.GetTileIndex(1, 0) and AIMap.GetTileIndex(-1, 0).
+ * @param end The tile from where the 'tile to be considered' will be
+ * exited. This is a relative tile, sovalid parameters are:
+ * AIMap.GetTileIndex(0, 1), AIMap.GetTileIndex(0, -1),
+ * AIMap.GetTileIndex(1, 0) and AIMap.GetTileIndex(-1, 0).
+ * @pre start != end.
+ * @pre slope must be a valid slope, i.e. one specified in AITile::Slope.
+ * @note Passing data that would be invalid in-game, e.g. existing containing
+ * road parts that can not be build on a tile with the given slope,
+ * does not necessarily means that -1 is returned, i.e. not all
+ * preconditions written here or assumed by the game are extensively
+ * checked to make sure the data entered is valid.
+ * @return 0 when the build parts do not connect, 1 when they do connect once
+ * they are build or 2 when building the first part automatically
+ * builds the second part. -1 means the preconditions are not met.
+ */
+ static int32 CanBuildConnectedRoadParts(AITile::Slope slope, struct Array *existing, TileIndex start, TileIndex end);
+
+ /**
+ * Lookup function for building road parts independend on whether the
+ * "building on slopes" setting is enabled or not.
+ * This implementation can be used for reasoning about an existing tile.
+ * @param tile The the tile to examine.
+ * @param start The tile from where "tile" will be entered.
+ * @param end The tile from where "tile" will be exited.
+ * @pre start != end.
+ * @pre tile != start.
+ * @pre tile != end.
+ * @pre AIMap.IsValidTile(tile).
+ * @pre AIMap.IsValidTile(start).
+ * @pre AIMap.IsValidTile(end).
+ * @pre AIMap.GetDistanceManhattanToTile(tile, start) == 1.
+ * @pre AIMap.GetDistanceManhattanToTile(tile, end) == 1.
+ * @return 0 when the build parts do not connect, 1 when they do connect once
+ * they are build or 2 when building the first part automatically
+ * builds the second part. -1 means the preconditions are not met.
+ */
+ static int32 CanBuildConnectedRoadPartsHere(TileIndex tile, TileIndex start, TileIndex end);
+
+ /**
+ * Count how many neighbours are road.
+ * @param tile The tile to check on.
+ * @pre AIMap::IsValidTile(tile).
+ * @return 0 means no neighbour road; max value is 4.
+ */
+ static int32 GetNeighbourRoadCount(TileIndex tile);
+
+ /**
+ * Gets the tile in front of a road depot.
+ * @param depot The road depot tile.
+ * @pre IsRoadDepotTile(depot).
+ * @return The tile in front of the depot.
+ */
+ static TileIndex GetRoadDepotFrontTile(TileIndex depot);
+
+ /**
+ * Gets the tile in front of a road station.
+ * @param station The road station tile.
+ * @pre IsRoadStationTile(station).
+ * @return The tile in front of the road station.
+ */
+ static TileIndex GetRoadStationFrontTile(TileIndex station);
+
+ /**
+ * Gets the tile at the back of a drive through road station.
+ * So, one side of the drive through station is retrieved with
+ * GetTileInFrontOfStation, the other with this function.
+ * @param station The road station tile.
+ * @pre IsDriveThroughRoadStationTile(station).
+ * @return The tile at the back of the drive through road station.
+ */
+ static TileIndex GetDriveThroughBackTile(TileIndex station);
+
+ /**
+ * Builds a road from the center of tile start to the center of tile end.
+ * @param start The start tile of the road.
+ * @param end The end tile of the road.
+ * @pre 'start' is not equal to 'end'.
+ * @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).
+ * @exception AIError::ERR_ALREADY_BUILT
+ * @exception AIError::ERR_LAND_SLOPED_WRONG
+ * @exception AIError::ERR_AREA_NOT_CLEAR
+ * @exception AIRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS
+ * @exception AIRoad::ERR_ROAD_WORKS_IN_PROGRESS
+ * @exception AIError::ERR_VEHICLE_IN_THE_WAY
+ * @return Whether the road has been/can be build or not.
+ */
+ static bool BuildRoad(TileIndex start, TileIndex end);
+
+ /**
+ * Builds a one-way road from the center of tile start to the center
+ * of tile end. If the road already exists, it is made one-way road.
+ * If the road already exists and is already one-way in this direction,
+ * the road is made two-way again. If the road already exists but is
+ * one-way in the other direction, it's made a 'no'-way road (it's
+ * forbidden to enter the tile from any direction).
+ * @param start The start tile of the road.
+ * @param end The end tile of the road.
+ * @pre 'start' is not equal to 'end'.
+ * @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 GetCurrentRoadType() == ROADTYPE_ROAD.
+ * @exception AIError::ERR_ALREADY_BUILT
+ * @exception AIError::ERR_LAND_SLOPED_WRONG
+ * @exception AIError::ERR_AREA_NOT_CLEAR
+ * @exception AIRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS
+ * @exception AIRoad::ERR_ROAD_WORKS_IN_PROGRESS
+ * @exception AIError::ERR_VEHICLE_IN_THE_WAY
+ * @return Whether the road has been/can be build or not.
+ */
+ static bool BuildOneWayRoad(TileIndex start, TileIndex end);
+
+ /**
+ * Builds a road from the edge of tile start to the edge of tile end (both
+ * included).
+ * @param start The start tile of the road.
+ * @param end The end tile of the road.
+ * @pre 'start' is not equal to 'end'.
+ * @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).
+ * @exception AIError::ERR_ALREADY_BUILT
+ * @exception AIError::ERR_LAND_SLOPED_WRONG
+ * @exception AIError::ERR_AREA_NOT_CLEAR
+ * @exception AIRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS
+ * @exception AIRoad::ERR_ROAD_WORKS_IN_PROGRESS
+ * @exception AIError::ERR_VEHICLE_IN_THE_WAY
+ * @return Whether the road has been/can be build or not.
+ */
+ static bool BuildRoadFull(TileIndex start, TileIndex end);
+
+ /**
+ * Builds a one-way road from the edge of tile start to the edge of tile end
+ * (both included). If the road already exists, it is made one-way road.
+ * If the road already exists and is already one-way in this direction,
+ * the road is made two-way again. If the road already exists but is
+ * one-way in the other direction, it's made a 'no'-way road (it's
+ * forbidden to enter the tile from any direction).
+ * @param start The start tile of the road.
+ * @param start The start tile of the road.
+ * @param end The end tile of the road.
+ * @pre 'start' is not equal to 'end'.
+ * @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 GetCurrentRoadType() == ROADTYPE_ROAD.
+ * @exception AIError::ERR_ALREADY_BUILT
+ * @exception AIError::ERR_LAND_SLOPED_WRONG
+ * @exception AIError::ERR_AREA_NOT_CLEAR
+ * @exception AIRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS
+ * @exception AIRoad::ERR_ROAD_WORKS_IN_PROGRESS
+ * @exception AIError::ERR_VEHICLE_IN_THE_WAY
+ * @return Whether the road has been/can be build or not.
+ */
+ static bool BuildOneWayRoadFull(TileIndex start, TileIndex end);
+
+ /**
+ * Builds a road depot.
+ * @param tile Place to build the depot.
+ * @param front The tile exactly in front of the depot.
+ * @pre AIMap::IsValidTile(tile).
+ * @pre AIMap::IsValidTile(front).
+ * @pre 'tile' is not equal to 'front', but in a straight line of it.
+ * @exception AIError::ERR_FLAT_LAND_REQUIRED
+ * @exception AIError::ERR_AREA_NOT_CLEAR
+ * @return Whether the road depot has been/can be build or not.
+ */
+ static bool BuildRoadDepot(TileIndex tile, TileIndex front);
+
+ /**
+ * Builds a road bus or truck station.
+ * @param tile Place to build the depot.
+ * @param front The tile exactly in front of the station.
+ * For drive-through stations either entrance side can be used.
+ * @param truck Whether to build a truck (true) or bus (false) station.
+ * @param drive_through Whether to make the station drive through or not.
+ * @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 AIMap::IsValidTile(front).
+ * @pre 'tile' is not equal to 'front', but in a straight line of it.
+ * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
+ * @exception AIError::ERR_AREA_NOT_CLEAR
+ * @exception AIError::ERR_FLAT_LAND_REQUIRED
+ * @exception AIRoad::ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION
+ * @exception AIRoad::ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD
+ * @exception AIError:ERR_VEHICLE_IN_THE_WAY
+ * @exception AIStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION
+ * @exception AIStation::ERR_STATION_TOO_MANY_STATIONS
+ * @exception AIStation::ERR_STATION_TOO_MANY_STATIONS_IN_TOWN
+ * @return Whether the station has been/can be build or not.
+ */
+ static bool BuildRoadStation(TileIndex tile, TileIndex front, bool truck, bool drive_through, bool join_adjacent);
+
+ /**
+ * Removes a road from the center of tile start to the center of tile end.
+ * @param start The start tile of the road.
+ * @param end The end tile of the road.
+ * @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).
+ * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
+ * @exception AIError::ERR_VEHICLE_IN_THE_WAY
+ * @exception AIRoad::ERR_ROAD_WORKS_IN_PROGRESS
+ * @return Whether the road has been/can be removed or not.
+ */
+ static bool RemoveRoad(TileIndex start, TileIndex end);
+
+ /**
+ * Removes a road from the edge of tile start to the edge of tile end (both
+ * included).
+ * @param start The start tile of the road.
+ * @param end The end tile of the road.
+ * @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).
+ * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
+ * @exception AIError::ERR_VEHICLE_IN_THE_WAY
+ * @exception AIRoad::ERR_ROAD_WORKS_IN_PROGRESS
+ * @return Whether the road has been/can be removed or not.
+ */
+ static bool RemoveRoadFull(TileIndex start, TileIndex end);
+
+ /**
+ * Removes a road depot.
+ * @param tile Place to remove the depot from.
+ * @pre AIMap::IsValidTile(tile).
+ * @pre Tile is a road depot.
+ * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
+ * @exception AIError::ERR_VEHICLE_IN_THE_WAY
+ * @return Whether the road depot has been/can be removed or not.
+ */
+ static bool RemoveRoadDepot(TileIndex tile);
+
+ /**
+ * Removes a road bus or truck station.
+ * @param tile Place to remove the station from.
+ * @pre AIMap::IsValidTile(tile).
+ * @pre Tile is a road station.
+ * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
+ * @exception AIError::ERR_VEHICLE_IN_THE_WAY
+ * @return Whether the station has been/can be removed or not.
+ */
+ static bool RemoveRoadStation(TileIndex tile);
+
+private:
+
+ /**
+ * Internal function used by Build(OneWay)Road(Full).
+ */
+ static bool _BuildRoadInternal(TileIndex start, TileIndex end, bool one_way, bool full);
+};
+
+#endif /* AI_ROAD_HPP */
diff --git a/src/ai/api/ai_road.hpp.sq b/src/ai/api/ai_road.hpp.sq
new file mode 100644
index 000000000..6439ae193
--- /dev/null
+++ b/src/ai/api/ai_road.hpp.sq
@@ -0,0 +1,73 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_road.hpp"
+
+namespace SQConvert {
+ /* Allow enums to be used as Squirrel parameters */
+ template <> AIRoad::ErrorMessages GetParam(ForceType<AIRoad::ErrorMessages>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIRoad::ErrorMessages)tmp; }
+ template <> int Return<AIRoad::ErrorMessages>(HSQUIRRELVM vm, AIRoad::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; }
+ template <> AIRoad::RoadType GetParam(ForceType<AIRoad::RoadType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIRoad::RoadType)tmp; }
+ template <> int Return<AIRoad::RoadType>(HSQUIRRELVM vm, AIRoad::RoadType res) { sq_pushinteger(vm, (int32)res); return 1; }
+
+ /* Allow AIRoad to be used as Squirrel parameter */
+ template <> AIRoad *GetParam(ForceType<AIRoad *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIRoad *)instance; }
+ template <> AIRoad &GetParam(ForceType<AIRoad &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIRoad *)instance; }
+ template <> const AIRoad *GetParam(ForceType<const AIRoad *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIRoad *)instance; }
+ template <> const AIRoad &GetParam(ForceType<const AIRoad &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIRoad *)instance; }
+ template <> int Return<AIRoad *>(HSQUIRRELVM vm, AIRoad *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIRoad", res, NULL, DefSQDestructorCallback<AIRoad>); return 1; }
+}; // namespace SQConvert
+
+void SQAIRoad_Register(Squirrel *engine) {
+ DefSQClass <AIRoad> SQAIRoad("AIRoad");
+ SQAIRoad.PreRegister(engine);
+ SQAIRoad.AddConstructor<void (AIRoad::*)(), 1>(engine, "x");
+
+ SQAIRoad.DefSQConst(engine, AIRoad::ERR_ROAD_BASE, "ERR_ROAD_BASE");
+ SQAIRoad.DefSQConst(engine, AIRoad::ERR_ROAD_WORKS_IN_PROGRESS, "ERR_ROAD_WORKS_IN_PROGRESS");
+ SQAIRoad.DefSQConst(engine, AIRoad::ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION, "ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION");
+ SQAIRoad.DefSQConst(engine, AIRoad::ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD, "ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD");
+ SQAIRoad.DefSQConst(engine, AIRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS, "ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS");
+ SQAIRoad.DefSQConst(engine, AIRoad::ROADTYPE_ROAD, "ROADTYPE_ROAD");
+ SQAIRoad.DefSQConst(engine, AIRoad::ROADTYPE_TRAM, "ROADTYPE_TRAM");
+ SQAIRoad.DefSQConst(engine, AIRoad::ROADTYPE_INVALID, "ROADTYPE_INVALID");
+
+ AIError::RegisterErrorMap(STR_ROAD_WORKS_IN_PROGRESS, AIRoad::ERR_ROAD_WORKS_IN_PROGRESS);
+ AIError::RegisterErrorMap(STR_DRIVE_THROUGH_ERROR_DIRECTION, AIRoad::ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION);
+ AIError::RegisterErrorMap(STR_DRIVE_THROUGH_ERROR_ON_TOWN_ROAD, AIRoad::ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD);
+ AIError::RegisterErrorMap(STR_ERR_ONEWAY_ROADS_CAN_T_HAVE_JUNCTION, AIRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS);
+
+ AIError::RegisterErrorMapString(AIRoad::ERR_ROAD_WORKS_IN_PROGRESS, "ERR_ROAD_WORKS_IN_PROGRESS");
+ AIError::RegisterErrorMapString(AIRoad::ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION, "ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION");
+ AIError::RegisterErrorMapString(AIRoad::ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD, "ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD");
+ AIError::RegisterErrorMapString(AIRoad::ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS, "ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS");
+
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::GetClassName, "GetClassName", 1, "x");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::IsRoadTile, "IsRoadTile", 2, "xi");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::IsRoadDepotTile, "IsRoadDepotTile", 2, "xi");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::IsRoadStationTile, "IsRoadStationTile", 2, "xi");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::IsDriveThroughRoadStationTile, "IsDriveThroughRoadStationTile", 2, "xi");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::IsRoadTypeAvailable, "IsRoadTypeAvailable", 2, "xi");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::GetCurrentRoadType, "GetCurrentRoadType", 1, "x");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::SetCurrentRoadType, "SetCurrentRoadType", 2, "xi");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::HasRoadType, "HasRoadType", 3, "xii");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::AreRoadTilesConnected, "AreRoadTilesConnected", 3, "xii");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::CanBuildConnectedRoadParts, "CanBuildConnectedRoadParts", 5, "xiaii");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::CanBuildConnectedRoadPartsHere, "CanBuildConnectedRoadPartsHere", 4, "xiii");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::GetNeighbourRoadCount, "GetNeighbourRoadCount", 2, "xi");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::GetRoadDepotFrontTile, "GetRoadDepotFrontTile", 2, "xi");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::GetRoadStationFrontTile, "GetRoadStationFrontTile", 2, "xi");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::GetDriveThroughBackTile, "GetDriveThroughBackTile", 2, "xi");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::BuildRoad, "BuildRoad", 3, "xii");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::BuildOneWayRoad, "BuildOneWayRoad", 3, "xii");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::BuildRoadFull, "BuildRoadFull", 3, "xii");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::BuildOneWayRoadFull, "BuildOneWayRoadFull", 3, "xii");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::BuildRoadDepot, "BuildRoadDepot", 3, "xii");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::BuildRoadStation, "BuildRoadStation", 6, "xiibbb");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::RemoveRoad, "RemoveRoad", 3, "xii");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::RemoveRoadFull, "RemoveRoadFull", 3, "xii");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::RemoveRoadDepot, "RemoveRoadDepot", 2, "xi");
+ SQAIRoad.DefSQStaticMethod(engine, &AIRoad::RemoveRoadStation, "RemoveRoadStation", 2, "xi");
+
+ SQAIRoad.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_sign.cpp b/src/ai/api/ai_sign.cpp
new file mode 100644
index 000000000..561b5c857
--- /dev/null
+++ b/src/ai/api/ai_sign.cpp
@@ -0,0 +1,73 @@
+/* $Id$ */
+
+/** @file ai_sign.cpp Implementation of AISign. */
+
+#include "ai_sign.hpp"
+#include "table/strings.h"
+#include "../ai_instance.hpp"
+#include "../../openttd.h"
+#include "../../command_func.h"
+#include "../../core/alloc_func.hpp"
+#include "../../signs_base.h"
+#include "../../string_func.h"
+#include "../../strings_func.h"
+#include "../../tile_map.h"
+#include "../../company_func.h"
+
+/* static */ SignID AISign::GetMaxSignID()
+{
+ return ::GetMaxSignIndex();
+}
+
+/* static */ bool AISign::IsValidSign(SignID sign_id)
+{
+ return ::IsValidSignID(sign_id) && ::GetSign(sign_id)->owner == _current_company;
+}
+
+/* static */ bool AISign::SetName(SignID sign_id, const char *name)
+{
+ EnforcePrecondition(false, IsValidSign(sign_id));
+ EnforcePrecondition(false, !::StrEmpty(name));
+ EnforcePreconditionCustomError(false, ::strlen(name) < MAX_LENGTH_SIGN_NAME_BYTES, AIError::ERR_PRECONDITION_STRING_TOO_LONG);
+
+ return AIObject::DoCommand(0, sign_id, 0, CMD_RENAME_SIGN, name);
+}
+
+/* static */ const char *AISign::GetName(SignID sign_id)
+{
+ if (!IsValidSign(sign_id)) return NULL;
+
+ static const int len = 64;
+ char *sign_name = MallocT<char>(len);
+
+ ::SetDParam(0, sign_id);
+ ::GetString(sign_name, STR_SIGN_NAME, &sign_name[len - 1]);
+
+ return sign_name;
+}
+
+/* static */ TileIndex AISign::GetLocation(SignID sign_id)
+{
+ if (!IsValidSign(sign_id)) return INVALID_TILE;
+
+ const Sign *sign = ::GetSign(sign_id);
+ return ::TileVirtXY(sign->x, sign->y);
+}
+
+/* static */ bool AISign::RemoveSign(SignID sign_id)
+{
+ EnforcePrecondition(false, IsValidSign(sign_id));
+ return AIObject::DoCommand(0, sign_id, 0, CMD_RENAME_SIGN, "");
+}
+
+/* static */ SignID AISign::BuildSign(TileIndex location, const char *text)
+{
+ EnforcePrecondition(INVALID_SIGN, ::IsValidTile(location));
+ EnforcePrecondition(INVALID_SIGN, !::StrEmpty(text));
+ EnforcePreconditionCustomError(false, ::strlen(text) < MAX_LENGTH_SIGN_NAME_BYTES, AIError::ERR_PRECONDITION_STRING_TOO_LONG);
+
+ if (!AIObject::DoCommand(location, 0, 0, CMD_PLACE_SIGN, text, &AIInstance::DoCommandReturnSignID)) return INVALID_SIGN;
+
+ /* In case of test-mode, we return SignID 0 */
+ return 0;
+}
diff --git a/src/ai/api/ai_sign.hpp b/src/ai/api/ai_sign.hpp
new file mode 100644
index 000000000..8bf7e0b79
--- /dev/null
+++ b/src/ai/api/ai_sign.hpp
@@ -0,0 +1,96 @@
+/* $Id$ */
+
+/** @file ai_sign.hpp Everything to query and build signs. */
+
+#ifndef AI_SIGN_HPP
+#define AI_SIGN_HPP
+
+#include "ai_object.hpp"
+#include "ai_error.hpp"
+#include "ai_company.hpp"
+
+/**
+ * Class that handles all sign related functions.
+ */
+class AISign : public AIObject {
+public:
+ static const char *GetClassName() { return "AISign"; }
+
+ /**
+ * All sign related error messages.
+ */
+ enum ErrorMessages {
+
+ /** Base for sign building related errors */
+ ERR_SIGN_BASE = AIError::ERR_CAT_SIGN << AIError::ERR_CAT_BIT_SIZE,
+
+ /** Too many signs have been placed */
+ ERR_SIGN_TOO_MANY_SIGNS, // [STR_2808_TOO_MANY_SIGNS]
+ };
+
+ /**
+ * Gets the maximum sign index; there are no valid signs with a higher index.
+ * @return The maximum sign index.
+ * @post Return value is always non-negative.
+ */
+ static SignID GetMaxSignID();
+
+ /**
+ * Checks whether the given sign index is valid.
+ * @param sign_id The index to check.
+ * @return True if and only if the sign is valid.
+ */
+ static bool IsValidSign(SignID sign_id);
+
+ /**
+ * Set the name of a sign.
+ * @param sign_id The sign to set the name for.
+ * @param name The name for the sign.
+ * @pre IsValidSign(sign_id).
+ * @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 and only if the name was changed.
+ */
+ static bool SetName(SignID sign_id, const char *name);
+
+ /**
+ * Get the name of the sign.
+ * @param sign_id The sign to get the name of.
+ * @pre IsValidSign(sign_id).
+ * @return The name of the sign.
+ */
+ static const char *GetName(SignID sign_id);
+
+ /**
+ * Gets the location of the sign.
+ * @param sign_id The sign to get the location of.
+ * @pre IsValidSign(sign_id).
+ * @return The location of the sign.
+ */
+ static TileIndex GetLocation(SignID sign_id);
+
+ /**
+ * Builds a sign on the map.
+ * @param location The place to build the sign.
+ * @param text The text to place on the sign.
+ * @pre AIMap::IsValidTile(location).
+ * @pre 'text' must have at least one character.
+ * @pre 'text' must have at most 30 characters.
+ * @exception AISign::ERR_SIGN_TOO_MANY_SIGNS
+ * @return The SignID of the build sign (use IsValidSign() to check for validity).
+ * In test-mode it returns 0 if successful, or any other value to indicate
+ * failure.
+ */
+ static SignID BuildSign(TileIndex location, const char *text);
+
+ /**
+ * Removes a sign from the map.
+ * @param sign_id The sign to remove.
+ * @pre IsValidSign(sign_id).
+ * @return True if and only if the sign has been removed.
+ */
+ static bool RemoveSign(SignID sign_id);
+};
+
+#endif /* AI_SIGN_HPP */
diff --git a/src/ai/api/ai_sign.hpp.sq b/src/ai/api/ai_sign.hpp.sq
new file mode 100644
index 000000000..4a46e1bd8
--- /dev/null
+++ b/src/ai/api/ai_sign.hpp.sq
@@ -0,0 +1,41 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_sign.hpp"
+
+namespace SQConvert {
+ /* Allow enums to be used as Squirrel parameters */
+ template <> AISign::ErrorMessages GetParam(ForceType<AISign::ErrorMessages>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AISign::ErrorMessages)tmp; }
+ template <> int Return<AISign::ErrorMessages>(HSQUIRRELVM vm, AISign::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; }
+
+ /* Allow AISign to be used as Squirrel parameter */
+ template <> AISign *GetParam(ForceType<AISign *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AISign *)instance; }
+ template <> AISign &GetParam(ForceType<AISign &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AISign *)instance; }
+ template <> const AISign *GetParam(ForceType<const AISign *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AISign *)instance; }
+ template <> const AISign &GetParam(ForceType<const AISign &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AISign *)instance; }
+ template <> int Return<AISign *>(HSQUIRRELVM vm, AISign *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AISign", res, NULL, DefSQDestructorCallback<AISign>); return 1; }
+}; // namespace SQConvert
+
+void SQAISign_Register(Squirrel *engine) {
+ DefSQClass <AISign> SQAISign("AISign");
+ SQAISign.PreRegister(engine);
+ SQAISign.AddConstructor<void (AISign::*)(), 1>(engine, "x");
+
+ SQAISign.DefSQConst(engine, AISign::ERR_SIGN_BASE, "ERR_SIGN_BASE");
+ SQAISign.DefSQConst(engine, AISign::ERR_SIGN_TOO_MANY_SIGNS, "ERR_SIGN_TOO_MANY_SIGNS");
+
+ AIError::RegisterErrorMap(STR_2808_TOO_MANY_SIGNS, AISign::ERR_SIGN_TOO_MANY_SIGNS);
+
+ AIError::RegisterErrorMapString(AISign::ERR_SIGN_TOO_MANY_SIGNS, "ERR_SIGN_TOO_MANY_SIGNS");
+
+ SQAISign.DefSQStaticMethod(engine, &AISign::GetClassName, "GetClassName", 1, "x");
+ SQAISign.DefSQStaticMethod(engine, &AISign::GetMaxSignID, "GetMaxSignID", 1, "x");
+ SQAISign.DefSQStaticMethod(engine, &AISign::IsValidSign, "IsValidSign", 2, "xi");
+ SQAISign.DefSQStaticMethod(engine, &AISign::SetName, "SetName", 3, "xis");
+ SQAISign.DefSQStaticMethod(engine, &AISign::GetName, "GetName", 2, "xi");
+ SQAISign.DefSQStaticMethod(engine, &AISign::GetLocation, "GetLocation", 2, "xi");
+ SQAISign.DefSQStaticMethod(engine, &AISign::BuildSign, "BuildSign", 3, "xis");
+ SQAISign.DefSQStaticMethod(engine, &AISign::RemoveSign, "RemoveSign", 2, "xi");
+
+ SQAISign.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_station.cpp b/src/ai/api/ai_station.cpp
new file mode 100644
index 000000000..ab171a307
--- /dev/null
+++ b/src/ai/api/ai_station.cpp
@@ -0,0 +1,146 @@
+/* $Id$ */
+
+/** @file ai_station.cpp Implementation of AIStation. */
+
+#include "ai_station.hpp"
+#include "ai_cargo.hpp"
+#include "ai_map.hpp"
+#include "ai_town.hpp"
+#include "../../openttd.h"
+#include "../../command_func.h"
+#include "../../debug.h"
+#include "../../station_map.h"
+#include "../../variables.h"
+#include "../../string_func.h"
+#include "../../strings_func.h"
+#include "../../core/alloc_func.hpp"
+#include "../../company_func.h"
+#include "../../settings_type.h"
+#include "../../town.h"
+#include "table/strings.h"
+
+/* static */ bool AIStation::IsValidStation(StationID station_id)
+{
+ return ::IsValidStationID(station_id) && ::GetStation(station_id)->owner == _current_company;
+}
+
+/* static */ StationID AIStation::GetStationID(TileIndex tile)
+{
+ if (!::IsTileType(tile, MP_STATION)) return INVALID_STATION;
+ return ::GetStationIndex(tile);
+}
+
+/* static */ const char *AIStation::GetName(StationID station_id)
+{
+ if (!IsValidStation(station_id)) return NULL;
+
+ static const int len = 64;
+ char *station_name = MallocT<char>(len);
+
+ ::SetDParam(0, GetStation(station_id)->index);
+ ::GetString(station_name, STR_STATION, &station_name[len - 1]);
+ return station_name;
+}
+
+/* static */ bool AIStation::SetName(StationID station_id, const char *name)
+{
+ EnforcePrecondition(false, IsValidStation(station_id));
+ EnforcePrecondition(false, !::StrEmpty(name));
+ EnforcePreconditionCustomError(false, ::strlen(name) < MAX_LENGTH_STATION_NAME_BYTES, AIError::ERR_PRECONDITION_STRING_TOO_LONG);
+
+ return AIObject::DoCommand(0, station_id, 0, CMD_RENAME_STATION, name);
+}
+
+/* static */ TileIndex AIStation::GetLocation(StationID station_id)
+{
+ if (!IsValidStation(station_id)) return INVALID_TILE;
+
+ return ::GetStation(station_id)->xy;
+}
+
+/* static */ int32 AIStation::GetCargoWaiting(StationID station_id, CargoID cargo_id)
+{
+ if (!IsValidStation(station_id)) return -1;
+ if (!AICargo::IsValidCargo(cargo_id)) return -1;
+
+ return ::GetStation(station_id)->goods[cargo_id].cargo.Count();
+}
+
+/* static */ int32 AIStation::GetCargoRating(StationID station_id, CargoID cargo_id)
+{
+ if (!IsValidStation(station_id)) return -1;
+ if (!AICargo::IsValidCargo(cargo_id)) return -1;
+
+ return ::GetStation(station_id)->goods[cargo_id].rating * 101 >> 8;
+}
+
+/* static */ int32 AIStation::GetCoverageRadius(AIStation::StationType station_type)
+{
+ if (station_type == STATION_AIRPORT) {
+ DEBUG(ai, 0, "GetCoverageRadius(): coverage radius of airports needs to be requested via AIAirport::GetAirportCoverageRadius(), as it requires AirportType");
+ return -1;
+ }
+ if (CountBits(station_type) != 1) return -1;
+ if (!_settings_game.station.modified_catchment) return CA_UNMODIFIED;
+
+ switch (station_type) {
+ case STATION_TRAIN: return CA_TRAIN;
+ case STATION_TRUCK_STOP: return CA_TRUCK;
+ case STATION_BUS_STOP: return CA_BUS;
+ case STATION_DOCK: return CA_DOCK;
+ default: return CA_NONE;
+ }
+}
+
+/* static */ int32 AIStation::GetDistanceManhattanToTile(StationID station_id, TileIndex tile)
+{
+ if (!IsValidStation(station_id)) return -1;
+
+ return AIMap::DistanceManhattan(tile, GetLocation(station_id));
+}
+
+/* static */ int32 AIStation::GetDistanceSquareToTile(StationID station_id, TileIndex tile)
+{
+ if (!IsValidStation(station_id)) return -1;
+
+ return AIMap::DistanceSquare(tile, GetLocation(station_id));
+}
+
+/* static */ bool AIStation::IsWithinTownInfluence(StationID station_id, TownID town_id)
+{
+ if (!IsValidStation(station_id)) return false;
+
+ return AITown::IsWithinTownInfluence(town_id, GetLocation(station_id));
+}
+
+/* static */ bool AIStation::HasStationType(StationID station_id, StationType station_type)
+{
+ if (!IsValidStation(station_id)) return false;
+ if (CountBits(station_type) != 1) return false;
+
+ return (::GetStation(station_id)->facilities & station_type) != 0;
+}
+
+/* static */ bool AIStation::HasRoadType(StationID station_id, AIRoad::RoadType road_type)
+{
+ if (!IsValidStation(station_id)) return false;
+ if (!AIRoad::IsRoadTypeAvailable(road_type)) return false;
+
+ ::RoadTypes r = RoadTypeToRoadTypes((::RoadType)road_type);
+
+ for (const RoadStop *rs = ::GetStation(station_id)->GetPrimaryRoadStop(ROADSTOP_BUS); rs != NULL; rs = rs->next) {
+ if ((::GetRoadTypes(rs->xy) & r) != 0) return true;
+ }
+ for (const RoadStop *rs = ::GetStation(station_id)->GetPrimaryRoadStop(ROADSTOP_TRUCK); rs != NULL; rs = rs->next) {
+ if ((::GetRoadTypes(rs->xy) & r) != 0) return true;
+ }
+
+ return false;
+}
+
+/* static */ TownID AIStation::GetNearestTown(StationID station_id)
+{
+ if (!IsValidStation(station_id)) return INVALID_TOWN;
+
+ return ::GetStation(station_id)->town->index;
+}
diff --git a/src/ai/api/ai_station.hpp b/src/ai/api/ai_station.hpp
new file mode 100644
index 000000000..6e263c912
--- /dev/null
+++ b/src/ai/api/ai_station.hpp
@@ -0,0 +1,184 @@
+/* $Id$ */
+
+/** @file ai_station.hpp Everything to query and build stations. */
+
+#ifndef AI_STATION_HPP
+#define AI_STATION_HPP
+
+#include "ai_object.hpp"
+#include "ai_error.hpp"
+#include "ai_road.hpp"
+
+/**
+ * Class that handles all station related functions.
+ */
+class AIStation : public AIObject {
+public:
+ static const char *GetClassName() { return "AIStation"; }
+
+ /**
+ * All station related error messages.
+ */
+ enum ErrorMessages {
+ /** Base for station related errors */
+ ERR_STATION_BASE = AIError::ERR_CAT_STATION << AIError::ERR_CAT_BIT_SIZE,
+
+ /** The station size exceeds the station spread */
+ ERR_STATION_TOO_LARGE, // [STR_306C_STATION_TOO_SPREAD_OUT]
+
+ /** The station is build too close to another station, airport or dock */
+ ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION, // [STR_300D_TOO_CLOSE_TO_ANOTHER_AIRPORT, STR_3009_TOO_CLOSE_TO_ANOTHER_STATION, STR_304C_TOO_CLOSE_TO_ANOTHER_DOCK]
+
+ /** There are too many stations, airports and docks in the game */
+ ERR_STATION_TOO_MANY_STATIONS, // [STR_3008_TOO_MANY_STATIONS_LOADING, STR_TOO_MANY_TRUCK_STOPS, STR_TOO_MANY_BUS_STOPS]
+
+ /** There are too many stations, airports of docks in a town */
+ ERR_STATION_TOO_MANY_STATIONS_IN_TOWN, // [STR_3007_TOO_MANY_STATIONS_LOADING]
+ };
+
+ /**
+ * Type of stations known in the game.
+ */
+ enum StationType {
+ /* Values are important, as they represent the internal state of the game. */
+ STATION_TRAIN = 0x01, //!< Train station
+ STATION_TRUCK_STOP = 0x02, //!< Truck station
+ STATION_BUS_STOP = 0x04, //!< Bus station
+ STATION_AIRPORT = 0x08, //!< Airport
+ STATION_DOCK = 0x10, //!< Dock
+ STATION_ANY = 0x1F, //!< All station types
+ };
+
+ /**
+ * Checks whether the given station is valid and owned by you.
+ * @param station_id The station to check.
+ * @return True if and only if the station is valid.
+ */
+ static bool IsValidStation(StationID station_id);
+
+ /**
+ * Get the StationID of a tile, if there is a station.
+ * @param tile The tile to find the stationID of
+ * @return StationID of the station.
+ * @post Use IsValidStation() to see if the station is valid.
+ */
+ static StationID GetStationID(TileIndex tile);
+
+ /**
+ * Get the name of a station.
+ * @param station_id The station to get the name of.
+ * @pre IsValidStation(station_id).
+ * @return The name of the station.
+ */
+ static const char *GetName(StationID station_id);
+
+ /**
+ * Set the name this station.
+ * @param station_id The station to set the name of.
+ * @param name The new name of the station.
+ * @pre IsValidStation(station_id).
+ * @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(StationID station_id, const char *name);
+
+ /**
+ * Get the current location of a station.
+ * @param station_id The station to get the location of.
+ * @pre IsValidStation(station_id).
+ * @return The tile the station is currently on.
+ */
+ static TileIndex GetLocation(StationID station_id);
+
+ /**
+ * See how much cargo there is waiting on a station.
+ * @param station_id The station to get the cargo-waiting of.
+ * @param cargo_id The cargo to get the cargo-waiting of.
+ * @pre IsValidStation(station_id).
+ * @pre IsValidCargo(cargo_id).
+ * @return The amount of units waiting at the station.
+ */
+ static int32 GetCargoWaiting(StationID station_id, CargoID cargo_id);
+
+ /**
+ * See how high the rating is of a cargo on a station.
+ * @param station_id The station to get the cargo-rating of.
+ * @param cargo_id The cargo to get the cargo-rating of.
+ * @pre IsValidStation(station_id).
+ * @pre IsValidCargo(cargo_id).
+ * @return The rating in percent of the cargo on the station.
+ */
+ static int32 GetCargoRating(StationID station_id, CargoID cargo_id);
+
+ /**
+ * Get the coverage radius of this type of station.
+ * @param station_type The type of station.
+ * @return The radius in tiles.
+ */
+ static int32 GetCoverageRadius(AIStation::StationType station_type);
+
+ /**
+ * Get the manhattan distance from the tile to the AIStation::GetLocation()
+ * of the station.
+ * @param station_id The station to get the distance to.
+ * @param tile The tile to get the distance to.
+ * @pre IsValidStation(station_id).
+ * @return The distance between station and tile.
+ */
+ static int32 GetDistanceManhattanToTile(StationID station_id, TileIndex tile);
+
+ /**
+ * Get the square distance from the tile to the AIStation::GetLocation()
+ * of the station.
+ * @param station_id The station to get the distance to.
+ * @param tile The tile to get the distance to.
+ * @pre IsValidStation(station_id).
+ * @return The distance between station and tile.
+ */
+ static int32 GetDistanceSquareToTile(StationID station_id, TileIndex tile);
+
+ /**
+ * Find out if this station is within the rating influence of a town.
+ * Stations within the radius influence the rating of the town.
+ * @param station_id The station to check.
+ * @param town_id The town to check.
+ * @return True if the tile is within the rating influence of the town.
+ */
+ static bool IsWithinTownInfluence(StationID station_id, TownID town_id);
+
+ /**
+ * Check if any part of the station contains a station of the type
+ * StationType
+ * @param station_id The station to look at.
+ * @param station_type The StationType to look for.
+ * @return True if the station has a station part of the type StationType.
+ */
+ static bool HasStationType(StationID station_id, StationType station_type);
+
+ /**
+ * Check if any part of the station contains a station of the type
+ * RoadType.
+ * @param station_id The station to look at.
+ * @param road_type The RoadType to look for.
+ * @return True if the station has a station part of the type RoadType.
+ */
+ static bool HasRoadType(StationID station_id, AIRoad::RoadType road_type);
+
+ /**
+ * Get the town that was nearest to the given station when the station was built.
+ * @param station_id The station to look at.
+ * @return The TownID of the town whose center tile was closest to the station
+ * at the time the station was built.
+ * @note There is no guarantee that the station is even near the returned town
+ * nor that the returns town is closest to the station now. A station that was
+ * 'walked' to the other end of the map will still return the same town. Also,
+ * towns grow, towns change. So don't depend on this value too much.
+ */
+ static TownID GetNearestTown(StationID station_id);
+};
+
+DECLARE_ENUM_AS_BIT_SET(AIStation::StationType);
+
+#endif /* AI_STATION_HPP */
diff --git a/src/ai/api/ai_station.hpp.sq b/src/ai/api/ai_station.hpp.sq
new file mode 100644
index 000000000..f752d8d9a
--- /dev/null
+++ b/src/ai/api/ai_station.hpp.sq
@@ -0,0 +1,69 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_station.hpp"
+
+namespace SQConvert {
+ /* Allow enums to be used as Squirrel parameters */
+ template <> AIStation::ErrorMessages GetParam(ForceType<AIStation::ErrorMessages>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIStation::ErrorMessages)tmp; }
+ template <> int Return<AIStation::ErrorMessages>(HSQUIRRELVM vm, AIStation::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; }
+ template <> AIStation::StationType GetParam(ForceType<AIStation::StationType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIStation::StationType)tmp; }
+ template <> int Return<AIStation::StationType>(HSQUIRRELVM vm, AIStation::StationType res) { sq_pushinteger(vm, (int32)res); return 1; }
+
+ /* Allow AIStation to be used as Squirrel parameter */
+ template <> AIStation *GetParam(ForceType<AIStation *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIStation *)instance; }
+ template <> AIStation &GetParam(ForceType<AIStation &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIStation *)instance; }
+ template <> const AIStation *GetParam(ForceType<const AIStation *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIStation *)instance; }
+ template <> const AIStation &GetParam(ForceType<const AIStation &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIStation *)instance; }
+ template <> int Return<AIStation *>(HSQUIRRELVM vm, AIStation *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIStation", res, NULL, DefSQDestructorCallback<AIStation>); return 1; }
+}; // namespace SQConvert
+
+void SQAIStation_Register(Squirrel *engine) {
+ DefSQClass <AIStation> SQAIStation("AIStation");
+ SQAIStation.PreRegister(engine);
+ SQAIStation.AddConstructor<void (AIStation::*)(), 1>(engine, "x");
+
+ SQAIStation.DefSQConst(engine, AIStation::ERR_STATION_BASE, "ERR_STATION_BASE");
+ SQAIStation.DefSQConst(engine, AIStation::ERR_STATION_TOO_LARGE, "ERR_STATION_TOO_LARGE");
+ SQAIStation.DefSQConst(engine, AIStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION, "ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION");
+ SQAIStation.DefSQConst(engine, AIStation::ERR_STATION_TOO_MANY_STATIONS, "ERR_STATION_TOO_MANY_STATIONS");
+ SQAIStation.DefSQConst(engine, AIStation::ERR_STATION_TOO_MANY_STATIONS_IN_TOWN, "ERR_STATION_TOO_MANY_STATIONS_IN_TOWN");
+ SQAIStation.DefSQConst(engine, AIStation::STATION_TRAIN, "STATION_TRAIN");
+ SQAIStation.DefSQConst(engine, AIStation::STATION_TRUCK_STOP, "STATION_TRUCK_STOP");
+ SQAIStation.DefSQConst(engine, AIStation::STATION_BUS_STOP, "STATION_BUS_STOP");
+ SQAIStation.DefSQConst(engine, AIStation::STATION_AIRPORT, "STATION_AIRPORT");
+ SQAIStation.DefSQConst(engine, AIStation::STATION_DOCK, "STATION_DOCK");
+ SQAIStation.DefSQConst(engine, AIStation::STATION_ANY, "STATION_ANY");
+
+ AIError::RegisterErrorMap(STR_306C_STATION_TOO_SPREAD_OUT, AIStation::ERR_STATION_TOO_LARGE);
+ AIError::RegisterErrorMap(STR_300D_TOO_CLOSE_TO_ANOTHER_AIRPORT, AIStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION);
+ AIError::RegisterErrorMap(STR_3009_TOO_CLOSE_TO_ANOTHER_STATION, AIStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION);
+ AIError::RegisterErrorMap(STR_304C_TOO_CLOSE_TO_ANOTHER_DOCK, AIStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION);
+ AIError::RegisterErrorMap(STR_3008_TOO_MANY_STATIONS_LOADING, AIStation::ERR_STATION_TOO_MANY_STATIONS);
+ AIError::RegisterErrorMap(STR_TOO_MANY_TRUCK_STOPS, AIStation::ERR_STATION_TOO_MANY_STATIONS);
+ AIError::RegisterErrorMap(STR_TOO_MANY_BUS_STOPS, AIStation::ERR_STATION_TOO_MANY_STATIONS);
+ AIError::RegisterErrorMap(STR_3007_TOO_MANY_STATIONS_LOADING, AIStation::ERR_STATION_TOO_MANY_STATIONS_IN_TOWN);
+
+ AIError::RegisterErrorMapString(AIStation::ERR_STATION_TOO_LARGE, "ERR_STATION_TOO_LARGE");
+ AIError::RegisterErrorMapString(AIStation::ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION, "ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION");
+ AIError::RegisterErrorMapString(AIStation::ERR_STATION_TOO_MANY_STATIONS, "ERR_STATION_TOO_MANY_STATIONS");
+ AIError::RegisterErrorMapString(AIStation::ERR_STATION_TOO_MANY_STATIONS_IN_TOWN, "ERR_STATION_TOO_MANY_STATIONS_IN_TOWN");
+
+ SQAIStation.DefSQStaticMethod(engine, &AIStation::GetClassName, "GetClassName", 1, "x");
+ SQAIStation.DefSQStaticMethod(engine, &AIStation::IsValidStation, "IsValidStation", 2, "xi");
+ SQAIStation.DefSQStaticMethod(engine, &AIStation::GetStationID, "GetStationID", 2, "xi");
+ SQAIStation.DefSQStaticMethod(engine, &AIStation::GetName, "GetName", 2, "xi");
+ SQAIStation.DefSQStaticMethod(engine, &AIStation::SetName, "SetName", 3, "xis");
+ SQAIStation.DefSQStaticMethod(engine, &AIStation::GetLocation, "GetLocation", 2, "xi");
+ SQAIStation.DefSQStaticMethod(engine, &AIStation::GetCargoWaiting, "GetCargoWaiting", 3, "xii");
+ SQAIStation.DefSQStaticMethod(engine, &AIStation::GetCargoRating, "GetCargoRating", 3, "xii");
+ SQAIStation.DefSQStaticMethod(engine, &AIStation::GetCoverageRadius, "GetCoverageRadius", 2, "xi");
+ SQAIStation.DefSQStaticMethod(engine, &AIStation::GetDistanceManhattanToTile, "GetDistanceManhattanToTile", 3, "xii");
+ SQAIStation.DefSQStaticMethod(engine, &AIStation::GetDistanceSquareToTile, "GetDistanceSquareToTile", 3, "xii");
+ SQAIStation.DefSQStaticMethod(engine, &AIStation::IsWithinTownInfluence, "IsWithinTownInfluence", 3, "xii");
+ SQAIStation.DefSQStaticMethod(engine, &AIStation::HasStationType, "HasStationType", 3, "xii");
+ SQAIStation.DefSQStaticMethod(engine, &AIStation::HasRoadType, "HasRoadType", 3, "xii");
+ SQAIStation.DefSQStaticMethod(engine, &AIStation::GetNearestTown, "GetNearestTown", 2, "xi");
+
+ SQAIStation.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_stationlist.cpp b/src/ai/api/ai_stationlist.cpp
new file mode 100644
index 000000000..43cb33878
--- /dev/null
+++ b/src/ai/api/ai_stationlist.cpp
@@ -0,0 +1,29 @@
+/* $Id$ */
+
+/** @file ai_stationlist.cpp Implementation of AIStationList and friends. */
+
+#include "ai_stationlist.hpp"
+#include "ai_vehicle.hpp"
+#include "../../openttd.h"
+#include "../../company_func.h"
+#include "../../station_base.h"
+#include "../../vehicle_base.h"
+
+AIStationList::AIStationList(AIStation::StationType station_type)
+{
+ Station *st;
+ FOR_ALL_STATIONS(st) {
+ if (st->owner == _current_company && (st->facilities & station_type) != 0) this->AddItem(st->index);
+ }
+}
+
+AIStationList_Vehicle::AIStationList_Vehicle(VehicleID vehicle_id)
+{
+ if (!AIVehicle::IsValidVehicle(vehicle_id)) return;
+
+ Vehicle *v = ::GetVehicle(vehicle_id);
+
+ for (Order *o = v->GetFirstOrder(); o != NULL; o = o->next) {
+ if (o->IsType(OT_GOTO_STATION)) this->AddItem(o->GetDestination());
+ }
+}
diff --git a/src/ai/api/ai_stationlist.hpp b/src/ai/api/ai_stationlist.hpp
new file mode 100644
index 000000000..b6b1a7d1f
--- /dev/null
+++ b/src/ai/api/ai_stationlist.hpp
@@ -0,0 +1,39 @@
+/* $Id$ */
+
+/** @file ai_stationlist.hpp List all the stations (you own). */
+
+#ifndef AI_STATIONLIST_HPP
+#define AI_STATIONLIST_HPP
+
+#include "ai_abstractlist.hpp"
+#include "ai_station.hpp"
+
+/**
+ * Creates a list of stations of which you are the owner.
+ * @ingroup AIList
+ */
+class AIStationList : public AIAbstractList {
+public:
+ static const char *GetClassName() { return "AIStationList"; }
+
+ /**
+ * @param station_type The type of station to make a list of stations for.
+ */
+ AIStationList(AIStation::StationType station_type);
+};
+
+/**
+ * Creates a list of stations which the vehicle has in its orders.
+ * @ingroup AIList
+ */
+class AIStationList_Vehicle : public AIAbstractList {
+public:
+ static const char *GetClassName() { return "AIStationList_Vehicle"; }
+
+ /**
+ * @param vehicle_id The vehicle to get the list of stations he has in its orders from.
+ */
+ AIStationList_Vehicle(VehicleID vehicle_id);
+};
+
+#endif /* AI_STATIONLIST_HPP */
diff --git a/src/ai/api/ai_stationlist.hpp.sq b/src/ai/api/ai_stationlist.hpp.sq
new file mode 100644
index 000000000..d3e5bcd4c
--- /dev/null
+++ b/src/ai/api/ai_stationlist.hpp.sq
@@ -0,0 +1,42 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_stationlist.hpp"
+
+namespace SQConvert {
+ /* Allow AIStationList to be used as Squirrel parameter */
+ template <> AIStationList *GetParam(ForceType<AIStationList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIStationList *)instance; }
+ template <> AIStationList &GetParam(ForceType<AIStationList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIStationList *)instance; }
+ template <> const AIStationList *GetParam(ForceType<const AIStationList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIStationList *)instance; }
+ template <> const AIStationList &GetParam(ForceType<const AIStationList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIStationList *)instance; }
+ template <> int Return<AIStationList *>(HSQUIRRELVM vm, AIStationList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIStationList", res, NULL, DefSQDestructorCallback<AIStationList>); return 1; }
+}; // namespace SQConvert
+
+void SQAIStationList_Register(Squirrel *engine) {
+ DefSQClass <AIStationList> SQAIStationList("AIStationList");
+ SQAIStationList.PreRegister(engine, "AIAbstractList");
+ SQAIStationList.AddConstructor<void (AIStationList::*)(AIStation::StationType station_type), 2>(engine, "xi");
+
+ SQAIStationList.DefSQStaticMethod(engine, &AIStationList::GetClassName, "GetClassName", 1, "x");
+
+ SQAIStationList.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AIStationList_Vehicle to be used as Squirrel parameter */
+ template <> AIStationList_Vehicle *GetParam(ForceType<AIStationList_Vehicle *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIStationList_Vehicle *)instance; }
+ template <> AIStationList_Vehicle &GetParam(ForceType<AIStationList_Vehicle &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIStationList_Vehicle *)instance; }
+ template <> const AIStationList_Vehicle *GetParam(ForceType<const AIStationList_Vehicle *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIStationList_Vehicle *)instance; }
+ template <> const AIStationList_Vehicle &GetParam(ForceType<const AIStationList_Vehicle &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIStationList_Vehicle *)instance; }
+ template <> int Return<AIStationList_Vehicle *>(HSQUIRRELVM vm, AIStationList_Vehicle *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIStationList_Vehicle", res, NULL, DefSQDestructorCallback<AIStationList_Vehicle>); return 1; }
+}; // namespace SQConvert
+
+void SQAIStationList_Vehicle_Register(Squirrel *engine) {
+ DefSQClass <AIStationList_Vehicle> SQAIStationList_Vehicle("AIStationList_Vehicle");
+ SQAIStationList_Vehicle.PreRegister(engine, "AIAbstractList");
+ SQAIStationList_Vehicle.AddConstructor<void (AIStationList_Vehicle::*)(VehicleID vehicle_id), 2>(engine, "xi");
+
+ SQAIStationList_Vehicle.DefSQStaticMethod(engine, &AIStationList_Vehicle::GetClassName, "GetClassName", 1, "x");
+
+ SQAIStationList_Vehicle.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_subsidy.cpp b/src/ai/api/ai_subsidy.cpp
new file mode 100644
index 000000000..893b65e7d
--- /dev/null
+++ b/src/ai/api/ai_subsidy.cpp
@@ -0,0 +1,94 @@
+/* $Id$ */
+
+/** @file ai_subsidy.cpp Implementation of AISubsidy. */
+
+#include "ai_subsidy.hpp"
+#include "ai_error.hpp"
+#include "ai_company.hpp"
+#include "ai_date.hpp"
+#include "../../openttd.h"
+#include "../../economy_func.h"
+#include "../../station_base.h"
+#include "../../cargotype.h"
+
+/* static */ bool AISubsidy::IsValidSubsidy(SubsidyID subsidy_id)
+{
+ return subsidy_id < lengthof(_subsidies) && _subsidies[subsidy_id].cargo_type != CT_INVALID;
+}
+
+/* static */ bool AISubsidy::IsAwarded(SubsidyID subsidy_id)
+{
+ if (!IsValidSubsidy(subsidy_id)) return false;
+
+ return _subsidies[subsidy_id].age >= 12;
+}
+
+/* static */ AICompany::CompanyID AISubsidy::GetAwardedTo(SubsidyID subsidy_id)
+{
+ if (!IsAwarded(subsidy_id)) return AICompany::INVALID_COMPANY;
+
+ return (AICompany::CompanyID)((byte)GetStation(_subsidies[subsidy_id].from)->owner);
+}
+
+/* static */ int32 AISubsidy::GetExpireDate(SubsidyID subsidy_id)
+{
+ if (!IsValidSubsidy(subsidy_id)) return -1;
+
+ int year = AIDate::GetYear(AIDate::GetCurrentDate());
+ int month = AIDate::GetMonth(AIDate::GetCurrentDate());
+
+ if (IsAwarded(subsidy_id)) {
+ month += 24 - _subsidies[subsidy_id].age;
+ } else {
+ month += 12 - _subsidies[subsidy_id].age;
+ }
+
+ year += (month - 1) / 12;
+ month = ((month - 1) % 12) + 1;
+
+ return AIDate::GetDate(year, month, 1);
+}
+
+/* static */ CargoID AISubsidy::GetCargoType(SubsidyID subsidy_id)
+{
+ if (!IsValidSubsidy(subsidy_id)) return CT_INVALID;
+
+ return _subsidies[subsidy_id].cargo_type;
+}
+
+/* static */ bool AISubsidy::SourceIsTown(SubsidyID subsidy_id)
+{
+ if (!IsValidSubsidy(subsidy_id) || IsAwarded(subsidy_id)) return false;
+
+ return GetCargo(GetCargoType(subsidy_id))->town_effect == TE_PASSENGERS ||
+ GetCargo(GetCargoType(subsidy_id))->town_effect == TE_MAIL;
+}
+
+/* static */ int32 AISubsidy::GetSource(SubsidyID subsidy_id)
+{
+ if (!IsValidSubsidy(subsidy_id)) return INVALID_STATION;
+
+ return _subsidies[subsidy_id].from;
+}
+
+/* static */ bool AISubsidy::DestinationIsTown(SubsidyID subsidy_id)
+{
+ if (!IsValidSubsidy(subsidy_id) || IsAwarded(subsidy_id)) return false;
+
+ switch (GetCargo(GetCargoType(subsidy_id))->town_effect) {
+ case TE_PASSENGERS:
+ case TE_MAIL:
+ case TE_GOODS:
+ case TE_FOOD:
+ return true;
+ default:
+ return false;
+ }
+}
+
+/* static */ int32 AISubsidy::GetDestination(SubsidyID subsidy_id)
+{
+ if (!IsValidSubsidy(subsidy_id)) return INVALID_STATION;
+
+ return _subsidies[subsidy_id].to;
+}
diff --git a/src/ai/api/ai_subsidy.hpp b/src/ai/api/ai_subsidy.hpp
new file mode 100644
index 000000000..7be86b0e8
--- /dev/null
+++ b/src/ai/api/ai_subsidy.hpp
@@ -0,0 +1,101 @@
+/* $Id$ */
+
+/** @file ai_subsidy.hpp Everything to query subsidies. */
+
+#ifndef AI_SUBSIDY_HPP
+#define AI_SUBSIDY_HPP
+
+#include "ai_object.hpp"
+#include "ai_company.hpp"
+
+/**
+ * Class that handles all subsidy related functions.
+ */
+class AISubsidy : public AIObject {
+public:
+ static const char *GetClassName() { return "AISubsidy"; }
+
+ /**
+ * Check whether this is a valid SubsidyID.
+ * @param subsidy_id The SubsidyID to check.
+ * @return True if and only if this subsidy is still valid.
+ */
+ static bool IsValidSubsidy(SubsidyID subsidy_id);
+
+ /**
+ * Checks whether this subsidy is already awarded to some company.
+ * @param subsidy_id The SubsidyID to check.
+ * @pre IsValidSubsidy(subsidy).
+ * @return True if and only if this subsidy is already awarded.
+ */
+ static bool IsAwarded(SubsidyID subsidy_id);
+
+ /**
+ * Get the company index of the company this subsidy is awarded to.
+ * @param subsidy_id The SubsidyID to check.
+ * @pre IsAwarded(subsidy_id).
+ * @return The companyindex of the company this subsidy is awarded to.
+ */
+ static AICompany::CompanyID GetAwardedTo(SubsidyID subsidy_id);
+
+ /**
+ * Get the date this subsidy expires. In case the subsidy is already
+ * awarded, return the date the subsidy expires, else, return the date the
+ * offer expires.
+ * @param subsidy_id The SubsidyID to check.
+ * @pre IsValidSubsidy(subsidy_id).
+ * @return The last valid date of this subsidy.
+ * @note The return value of this function will change if the subsidy is
+ * awarded.
+ */
+ static int32 GetExpireDate(SubsidyID subsidy_id);
+
+ /**
+ * Get the cargo type that has to be transported in order to be awarded this
+ * subsidy.
+ * @param subsidy_id The SubsidyID to check.
+ * @pre IsValidSubsidy(subsidy_id).
+ * @return The cargo type to transport.
+ */
+ static CargoID GetCargoType(SubsidyID subsidy_id);
+
+ /**
+ * Is the source of the subsidy a town or an industry.
+ * @param subsidy_id The SubsidyID to check.
+ * @pre IsValidSubsidy(subsidy_id) && !IsAwarded(subsidy_id).
+ * @return True if the source is a town, false if it is an industry.
+ */
+ static bool SourceIsTown(SubsidyID subsidy_id);
+
+ /**
+ * Return the source TownID/IndustryID/StationID the subsidy is for.
+ * 1) IsAwarded(subsidy_id) -> return the StationID the subsidy is awarded to.
+ * 2) !IsAwarded(subsidy_id) && SourceIsTown(subsidy_id) -> return the TownID.
+ * 3) !IsAwarded(subsidy_id) && !SourceIsTown(subsidy_id) -> return the IndustryID.
+ * @param subsidy_id The SubsidyID to check.
+ * @pre IsValidSubsidy(subsidy_id).
+ * @return One of TownID/IndustryID/StationID.
+ */
+ static int32 GetSource(SubsidyID subsidy_id);
+
+ /**
+ * Is the destination of the subsidy a town or an industry.
+ * @param subsidy_id The SubsidyID to check.
+ * @pre IsValidSubsidy(subsidy_id) && !IsAwarded(subsidy_id).
+ * @return True if the destination is a town, false if it is an industry.
+ */
+ static bool DestinationIsTown(SubsidyID subsidy_id);
+
+ /**
+ * Return the destination TownID/IndustryID/StationID the subsidy is for.
+ * 1) IsAwarded(subsidy_id) -> return the StationID the subsidy is awarded to.
+ * 2) !IsAwarded(subsidy_id) && SourceIsTown(subsidy_id) -> return the TownID.
+ * 3) !IsAwarded(subsidy_id) && !SourceIsTown(subsidy_id) -> return the IndustryID.
+ * @param subsidy_id the SubsidyID to check.
+ * @pre IsValidSubsidy(subsidy_id).
+ * @return One of TownID/IndustryID/StationID.
+ */
+ static int32 GetDestination(SubsidyID subsidy_id);
+};
+
+#endif /* AI_SUBSIDY_HPP */
diff --git a/src/ai/api/ai_subsidy.hpp.sq b/src/ai/api/ai_subsidy.hpp.sq
new file mode 100644
index 000000000..41ed75077
--- /dev/null
+++ b/src/ai/api/ai_subsidy.hpp.sq
@@ -0,0 +1,32 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_subsidy.hpp"
+
+namespace SQConvert {
+ /* Allow AISubsidy to be used as Squirrel parameter */
+ template <> AISubsidy *GetParam(ForceType<AISubsidy *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AISubsidy *)instance; }
+ template <> AISubsidy &GetParam(ForceType<AISubsidy &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AISubsidy *)instance; }
+ template <> const AISubsidy *GetParam(ForceType<const AISubsidy *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AISubsidy *)instance; }
+ template <> const AISubsidy &GetParam(ForceType<const AISubsidy &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AISubsidy *)instance; }
+ template <> int Return<AISubsidy *>(HSQUIRRELVM vm, AISubsidy *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AISubsidy", res, NULL, DefSQDestructorCallback<AISubsidy>); return 1; }
+}; // namespace SQConvert
+
+void SQAISubsidy_Register(Squirrel *engine) {
+ DefSQClass <AISubsidy> SQAISubsidy("AISubsidy");
+ SQAISubsidy.PreRegister(engine);
+ SQAISubsidy.AddConstructor<void (AISubsidy::*)(), 1>(engine, "x");
+
+ SQAISubsidy.DefSQStaticMethod(engine, &AISubsidy::GetClassName, "GetClassName", 1, "x");
+ SQAISubsidy.DefSQStaticMethod(engine, &AISubsidy::IsValidSubsidy, "IsValidSubsidy", 2, "xi");
+ SQAISubsidy.DefSQStaticMethod(engine, &AISubsidy::IsAwarded, "IsAwarded", 2, "xi");
+ SQAISubsidy.DefSQStaticMethod(engine, &AISubsidy::GetAwardedTo, "GetAwardedTo", 2, "xi");
+ SQAISubsidy.DefSQStaticMethod(engine, &AISubsidy::GetExpireDate, "GetExpireDate", 2, "xi");
+ SQAISubsidy.DefSQStaticMethod(engine, &AISubsidy::GetCargoType, "GetCargoType", 2, "xi");
+ SQAISubsidy.DefSQStaticMethod(engine, &AISubsidy::SourceIsTown, "SourceIsTown", 2, "xi");
+ SQAISubsidy.DefSQStaticMethod(engine, &AISubsidy::GetSource, "GetSource", 2, "xi");
+ SQAISubsidy.DefSQStaticMethod(engine, &AISubsidy::DestinationIsTown, "DestinationIsTown", 2, "xi");
+ SQAISubsidy.DefSQStaticMethod(engine, &AISubsidy::GetDestination, "GetDestination", 2, "xi");
+
+ SQAISubsidy.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_subsidylist.cpp b/src/ai/api/ai_subsidylist.cpp
new file mode 100644
index 000000000..8e5d96ad9
--- /dev/null
+++ b/src/ai/api/ai_subsidylist.cpp
@@ -0,0 +1,14 @@
+/* $Id$ */
+
+/** @file ai_subsidylist.cpp Implementation of AISubsidyList. */
+
+#include "ai_subsidylist.hpp"
+#include "ai_subsidy.hpp"
+#include "../../economy_func.h"
+
+AISubsidyList::AISubsidyList()
+{
+ for (uint i = 0; i < lengthof(_subsidies); i++) {
+ if (AISubsidy::IsValidSubsidy(i)) this->AddItem(i);
+ }
+}
diff --git a/src/ai/api/ai_subsidylist.hpp b/src/ai/api/ai_subsidylist.hpp
new file mode 100644
index 000000000..dde3ba351
--- /dev/null
+++ b/src/ai/api/ai_subsidylist.hpp
@@ -0,0 +1,20 @@
+/* $Id$ */
+
+/** @file ai_subsidylist.hpp List all the subsidies. */
+
+#ifndef AI_SUBSIDYLIST_HPP
+#define AI_SUBSIDYLIST_HPP
+
+#include "ai_abstractlist.hpp"
+
+/**
+ * Creates a list of all current subsidies.
+ * @ingroup AIList
+ */
+class AISubsidyList : public AIAbstractList {
+public:
+ static const char *GetClassName() { return "AISubsidyList"; }
+ AISubsidyList();
+};
+
+#endif /* AI_SUBSIDYLIST_HPP */
diff --git a/src/ai/api/ai_subsidylist.hpp.sq b/src/ai/api/ai_subsidylist.hpp.sq
new file mode 100644
index 000000000..db86a6ab6
--- /dev/null
+++ b/src/ai/api/ai_subsidylist.hpp.sq
@@ -0,0 +1,23 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_subsidylist.hpp"
+
+namespace SQConvert {
+ /* Allow AISubsidyList to be used as Squirrel parameter */
+ template <> AISubsidyList *GetParam(ForceType<AISubsidyList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AISubsidyList *)instance; }
+ template <> AISubsidyList &GetParam(ForceType<AISubsidyList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AISubsidyList *)instance; }
+ template <> const AISubsidyList *GetParam(ForceType<const AISubsidyList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AISubsidyList *)instance; }
+ template <> const AISubsidyList &GetParam(ForceType<const AISubsidyList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AISubsidyList *)instance; }
+ template <> int Return<AISubsidyList *>(HSQUIRRELVM vm, AISubsidyList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AISubsidyList", res, NULL, DefSQDestructorCallback<AISubsidyList>); return 1; }
+}; // namespace SQConvert
+
+void SQAISubsidyList_Register(Squirrel *engine) {
+ DefSQClass <AISubsidyList> SQAISubsidyList("AISubsidyList");
+ SQAISubsidyList.PreRegister(engine, "AIAbstractList");
+ SQAISubsidyList.AddConstructor<void (AISubsidyList::*)(), 1>(engine, "x");
+
+ SQAISubsidyList.DefSQStaticMethod(engine, &AISubsidyList::GetClassName, "GetClassName", 1, "x");
+
+ SQAISubsidyList.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_testmode.cpp b/src/ai/api/ai_testmode.cpp
new file mode 100644
index 000000000..1b09fa699
--- /dev/null
+++ b/src/ai/api/ai_testmode.cpp
@@ -0,0 +1,26 @@
+/* $Id$ */
+
+/** @file ai_testmode.cpp Implementation of AITestMode. */
+
+#include "ai_testmode.hpp"
+#include "../../command_type.h"
+
+bool AITestMode::ModeProc(TileIndex tile, uint32 p1, uint32 p2, uint procc, CommandCost costs)
+{
+ /* In test mode we only return 'false', telling the DoCommand it
+ * should stop after testing the command and return with that result. */
+ return false;
+}
+
+AITestMode::AITestMode()
+{
+ this->last_mode = this->GetDoCommandMode();
+ this->last_instance = this->GetDoCommandModeInstance();
+ this->SetDoCommandMode(&AITestMode::ModeProc, this);
+}
+
+AITestMode::~AITestMode()
+{
+ assert(this->GetDoCommandModeInstance() == this);
+ this->SetDoCommandMode(this->last_mode, this->last_instance);
+}
diff --git a/src/ai/api/ai_testmode.hpp b/src/ai/api/ai_testmode.hpp
new file mode 100644
index 000000000..c65c501d9
--- /dev/null
+++ b/src/ai/api/ai_testmode.hpp
@@ -0,0 +1,48 @@
+/* $Id$ */
+
+/** @file ai_testmode.hpp Switch the AI to Test Mode. */
+
+#ifndef AI_TESTMODE_HPP
+#define AI_TESTMODE_HPP
+
+#include "ai_object.hpp"
+
+/**
+ * Class to switch current mode to Test Mode.
+ * If you create an instance of this class, the mode will be switched to
+ * Testing. The original mode is stored and recovered from when ever the
+ * instance is destroyed.
+ * In Test mode all the commands you execute aren't really executed. The
+ * system only checks if it would be able to execute your requests, and what
+ * the cost would be.
+ */
+class AITestMode : public AIObject {
+public:
+ static const char *GetClassName() { return "AITestMode"; }
+
+private:
+ AIModeProc *last_mode;
+ AIObject *last_instance;
+
+protected:
+ /**
+ * The callback proc for Testing mode.
+ */
+ static bool ModeProc(TileIndex tile, uint32 p1, uint32 p2, uint procc, CommandCost costs);
+
+public:
+ /**
+ * Creating instance of this class switches the build mode to Testing.
+ * @note When the instance is destroyed, he restores the mode that was
+ * current when the instance was created!
+ */
+ AITestMode();
+
+ /**
+ * Destroying this instance reset the building mode to the mode it was
+ * in when the instance was created.
+ */
+ ~AITestMode();
+};
+
+#endif /* AI_TESTMODE_HPP */
diff --git a/src/ai/api/ai_testmode.hpp.sq b/src/ai/api/ai_testmode.hpp.sq
new file mode 100644
index 000000000..5343d8cab
--- /dev/null
+++ b/src/ai/api/ai_testmode.hpp.sq
@@ -0,0 +1,23 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_testmode.hpp"
+
+namespace SQConvert {
+ /* Allow AITestMode to be used as Squirrel parameter */
+ template <> AITestMode *GetParam(ForceType<AITestMode *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITestMode *)instance; }
+ template <> AITestMode &GetParam(ForceType<AITestMode &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITestMode *)instance; }
+ template <> const AITestMode *GetParam(ForceType<const AITestMode *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITestMode *)instance; }
+ template <> const AITestMode &GetParam(ForceType<const AITestMode &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITestMode *)instance; }
+ template <> int Return<AITestMode *>(HSQUIRRELVM vm, AITestMode *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AITestMode", res, NULL, DefSQDestructorCallback<AITestMode>); return 1; }
+}; // namespace SQConvert
+
+void SQAITestMode_Register(Squirrel *engine) {
+ DefSQClass <AITestMode> SQAITestMode("AITestMode");
+ SQAITestMode.PreRegister(engine);
+ SQAITestMode.AddConstructor<void (AITestMode::*)(), 1>(engine, "x");
+
+ SQAITestMode.DefSQStaticMethod(engine, &AITestMode::GetClassName, "GetClassName", 1, "x");
+
+ SQAITestMode.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_tile.cpp b/src/ai/api/ai_tile.cpp
new file mode 100644
index 000000000..868cc679a
--- /dev/null
+++ b/src/ai/api/ai_tile.cpp
@@ -0,0 +1,239 @@
+/* $Id$ */
+
+/** @file ai_tile.cpp Implementation of AITile. */
+
+#include "ai_tile.hpp"
+#include "ai_map.hpp"
+#include "ai_town.hpp"
+#include "../../openttd.h"
+#include "../../tile_map.h"
+#include "../../tile_cmd.h"
+#include "../../map_func.h"
+#include "../../variables.h"
+#include "../../station_func.h"
+#include "../../command_type.h"
+#include "../../settings_type.h"
+#include "../../company_func.h"
+#include "../../road_map.h"
+#include "../../water_map.h"
+#include "../../clear_map.h"
+#include "../../town.h"
+
+/* static */ bool AITile::IsBuildable(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return false;
+
+ switch (::GetTileType(tile)) {
+ default: return false;
+ case MP_CLEAR: return true;
+ case MP_TREES: return true;
+ case MP_WATER: return IsCoast(tile);
+ case MP_ROAD:
+ /* Tram bits aren't considered buildable */
+ if (::GetRoadTypes(tile) != ROADTYPES_ROAD) return false;
+ /* Depots and crossings aren't considered buildable */
+ if (::GetRoadTileType(tile) != ROAD_TILE_NORMAL) return false;
+ if (CountBits(::GetRoadBits(tile, ROADTYPE_ROAD)) != 1) return false;
+ if (::IsRoadOwner(tile, ROADTYPE_ROAD, OWNER_TOWN)) return true;
+ if (::IsRoadOwner(tile, ROADTYPE_ROAD, _current_company)) return true;
+ return false;
+ }
+}
+
+/* static */ bool AITile::IsBuildableRectangle(TileIndex tile, uint width, uint height)
+{
+ uint tx, ty;
+
+ tx = AIMap::GetTileX(tile);
+ ty = AIMap::GetTileY(tile);
+
+ for (uint x = tx; x < width + tx; x++) {
+ for (uint y = ty; y < height + ty; y++) {
+ if (!IsBuildable(AIMap::GetTileIndex(x, y))) return false;
+ }
+ }
+
+ return true;
+}
+
+/* static */ bool AITile::IsWaterTile(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return false;
+
+ return ::IsTileType(tile, MP_WATER) && !::IsCoast(tile);
+}
+
+/* static */ bool AITile::IsCoastTile(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return false;
+
+ return ::IsTileType(tile, MP_WATER) && ::IsCoast(tile);
+}
+
+/* static */ bool AITile::IsStationTile(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return false;
+
+ return ::IsTileType(tile, MP_STATION);
+}
+
+/* static */ bool AITile::IsSteepSlope(Slope slope)
+{
+ if (slope == SLOPE_INVALID) return false;
+
+ return ::IsSteepSlope((::Slope)slope);
+}
+
+/* static */ bool AITile::IsHalftileSlope(Slope slope)
+{
+ if (slope == SLOPE_INVALID) return false;
+
+ return ::IsHalftileSlope((::Slope)slope);
+}
+
+/* static */ bool AITile::HasTreeOnTile(TileIndex tile)
+{
+ return ::IsTileType(tile, MP_TREES);
+}
+
+/* static */ bool AITile::IsFarmTile(TileIndex tile)
+{
+ return (::IsTileType(tile, MP_CLEAR) && ::IsClearGround(tile, CLEAR_FIELDS));
+}
+
+/* static */ bool AITile::IsRockTile(TileIndex tile)
+{
+ return (::IsTileType(tile, MP_CLEAR) && ::IsClearGround(tile, CLEAR_ROCKS));
+}
+
+/* static */ bool AITile::IsRoughTile(TileIndex tile)
+{
+ return (::IsTileType(tile, MP_CLEAR) && ::IsClearGround(tile, CLEAR_ROUGH));
+}
+
+/* static */ bool AITile::IsSnowTile(TileIndex tile)
+{
+ return (::IsTileType(tile, MP_CLEAR) && ::IsClearGround(tile, CLEAR_SNOW));
+}
+
+/* static */ bool AITile::IsDesertTile(TileIndex tile)
+{
+ return (::IsTileType(tile, MP_CLEAR) && ::IsClearGround(tile, CLEAR_DESERT));
+}
+
+/* static */ AITile::Slope AITile::GetSlope(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return SLOPE_INVALID;
+
+ return (Slope)::GetTileSlope(tile, NULL);
+}
+
+/* static */ AITile::Slope AITile::GetComplementSlope(Slope slope)
+{
+ if (slope == SLOPE_INVALID) return SLOPE_INVALID;
+ if (IsSteepSlope(slope)) return SLOPE_INVALID;
+ if (IsHalftileSlope(slope)) return SLOPE_INVALID;
+
+ return (Slope)::ComplementSlope((::Slope)slope);
+}
+
+/* static */ int32 AITile::GetHeight(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return false;
+
+ return ::TileHeight(tile);
+}
+
+/* static */ AICompany::CompanyID AITile::GetOwner(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return AICompany::INVALID_COMPANY;
+ if (::IsTileType(tile, MP_HOUSE)) return AICompany::INVALID_COMPANY;
+ if (::IsTileType(tile, MP_INDUSTRY)) return AICompany::INVALID_COMPANY;
+
+ return AICompany::ResolveCompanyID((AICompany::CompanyID)(byte)::GetTileOwner(tile));
+}
+
+/* static */ bool AITile::HasTransportType(TileIndex tile, TransportType transport_type)
+{
+ if (!::IsValidTile(tile)) return false;
+
+ return GB(::GetTileTrackStatus(tile, (::TransportType)transport_type, UINT32_MAX), 0, 16) != 0;
+}
+
+/* static */ int32 AITile::GetCargoAcceptance(TileIndex tile, CargoID cargo_type, uint width, uint height, uint radius)
+{
+ if (!::IsValidTile(tile)) return false;
+
+ AcceptedCargo accepts;
+ ::GetAcceptanceAroundTiles(accepts, tile, width, height, _settings_game.station.modified_catchment ? radius : (uint)CA_UNMODIFIED);
+ return accepts[cargo_type];
+}
+
+/* static */ int32 AITile::GetCargoProduction(TileIndex tile, CargoID cargo_type, uint width, uint height, uint radius)
+{
+ if (!::IsValidTile(tile)) return false;
+
+ AcceptedCargo produced;
+ ::GetProductionAroundTiles(produced, tile, width, height, _settings_game.station.modified_catchment ? radius : (uint)CA_UNMODIFIED);
+ return produced[cargo_type];
+}
+
+/* static */ int32 AITile::GetDistanceManhattanToTile(TileIndex tile_from, TileIndex tile_to)
+{
+ return AIMap::DistanceManhattan(tile_from, tile_to);
+}
+
+/* static */ int32 AITile::GetDistanceSquareToTile(TileIndex tile_from, TileIndex tile_to)
+{
+ return AIMap::DistanceSquare(tile_from, tile_to);
+}
+
+/* static */ bool AITile::RaiseTile(TileIndex tile, int32 slope)
+{
+ EnforcePrecondition(false, ::IsValidTile(tile));
+
+ return AIObject::DoCommand(tile, slope, 1, CMD_TERRAFORM_LAND);
+}
+
+/* static */ bool AITile::LowerTile(TileIndex tile, int32 slope)
+{
+ EnforcePrecondition(false, ::IsValidTile(tile));
+
+ return AIObject::DoCommand(tile, slope, 0, CMD_TERRAFORM_LAND);
+}
+
+/* static */ bool AITile::DemolishTile(TileIndex tile)
+{
+ EnforcePrecondition(false, ::IsValidTile(tile));
+
+ return AIObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR);
+}
+
+/* static */ bool AITile::PlantTree(TileIndex tile)
+{
+ EnforcePrecondition(false, ::IsValidTile(tile));
+
+ return AIObject::DoCommand(tile, -1, tile, CMD_PLANT_TREE);
+}
+
+/* static */ bool AITile::PlantTreeRectangle(TileIndex tile, uint width, uint height)
+{
+ EnforcePrecondition(false, ::IsValidTile(tile));
+ EnforcePrecondition(false, width >= 1 && width <= 20);
+ EnforcePrecondition(false, height >= 1 && height <= 20);
+ TileIndex end_tile = tile + ::TileDiffXY(width - 1, height - 1);
+
+ return AIObject::DoCommand(tile, -1, end_tile, CMD_PLANT_TREE);
+}
+
+/* static */ bool AITile::IsWithinTownInfluence(TileIndex tile, TownID town_id)
+{
+ return AITown::IsWithinTownInfluence(town_id, tile);
+}
+
+/* static */ TownID AITile::GetClosestTown(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return INVALID_TOWN;
+
+ return ::ClosestTownFromTile(tile, UINT_MAX)->index;
+}
diff --git a/src/ai/api/ai_tile.hpp b/src/ai/api/ai_tile.hpp
new file mode 100644
index 000000000..298de75cb
--- /dev/null
+++ b/src/ai/api/ai_tile.hpp
@@ -0,0 +1,362 @@
+/* $Id$ */
+
+/** @file ai_tile.hpp Everything to query and manipulate tiles. */
+
+#ifndef AI_TILE_HPP
+#define AI_TILE_HPP
+
+#include "ai_abstractlist.hpp"
+#include "ai_error.hpp"
+#include "ai_company.hpp"
+
+/**
+ * Class that handles all tile related functions.
+ */
+class AITile : public AIObject {
+public:
+ static const char *GetClassName() { return "AITile"; }
+
+ /**
+ * Error messages related to modifying tiles.
+ */
+ enum ErrorMessages {
+
+ /** Base for tile related errors */
+ ERR_TILE_BASE = AIError::ERR_CAT_TILE << AIError::ERR_CAT_BIT_SIZE,
+
+ /** Tile can't be raised any higher */
+ ERR_TILE_TOO_HIGH, // [STR_1003_ALREADY_AT_SEA_LEVEL]
+
+ /** Tile can't be lowered any lower */
+ ERR_TILE_TOO_LOW, // [STR_1003_ALREADY_AT_SEA_LEVEL]
+ };
+
+ /**
+ * Enumeration for the slope-type (from slopes.h).
+ *
+ * This enumeration use the chars N, E, S, W corresponding the
+ * direction North, East, South and West. The top corner of a tile
+ * is the north-part of the tile.
+ */
+ enum Slope {
+ /* Values are important, as they represent the internal state of the game. */
+ SLOPE_FLAT = 0x00, //!< A flat tile
+ SLOPE_W = 0x01, //!< The west corner of the tile is raised
+ SLOPE_S = 0x02, //!< The south corner of the tile is raised
+ SLOPE_E = 0x04, //!< The east corner of the tile is raised
+ SLOPE_N = 0x08, //!< The north corner of the tile is raised
+ SLOPE_STEEP = 0x10, //!< Indicates the slope is steep
+ SLOPE_NW = SLOPE_N | SLOPE_W, //!< North and west corner are raised
+ SLOPE_SW = SLOPE_S | SLOPE_W, //!< South and west corner are raised
+ SLOPE_SE = SLOPE_S | SLOPE_E, //!< South and east corner are raised
+ SLOPE_NE = SLOPE_N | SLOPE_E, //!< North and east corner are raised
+ SLOPE_EW = SLOPE_E | SLOPE_W, //!< East and west corner are raised
+ SLOPE_NS = SLOPE_N | SLOPE_S, //!< North and south corner are raised
+ SLOPE_ELEVATED = SLOPE_N | SLOPE_E | SLOPE_S | SLOPE_W, //!< All corner are raised, similar to SLOPE_FLAT
+ SLOPE_NWS = SLOPE_N | SLOPE_W | SLOPE_S, //!< North, west and south corner are raised
+ SLOPE_WSE = SLOPE_W | SLOPE_S | SLOPE_E, //!< West, south and east corner are raised
+ SLOPE_SEN = SLOPE_S | SLOPE_E | SLOPE_N, //!< South, east and north corner are raised
+ SLOPE_ENW = SLOPE_E | SLOPE_N | SLOPE_W, //!< East, north and west corner are raised
+ SLOPE_STEEP_W = SLOPE_STEEP | SLOPE_NWS, //!< A steep slope falling to east (from west)
+ SLOPE_STEEP_S = SLOPE_STEEP | SLOPE_WSE, //!< A steep slope falling to north (from south)
+ SLOPE_STEEP_E = SLOPE_STEEP | SLOPE_SEN, //!< A steep slope falling to west (from east)
+ SLOPE_STEEP_N = SLOPE_STEEP | SLOPE_ENW, //!< A steep slope falling to south (from north)
+
+ SLOPE_INVALID = 0xFF, //!< An invalid slope
+ };
+
+ /**
+ * The different transport types a tile can have.
+ */
+ enum TransportType {
+ /* Values are important, as they represent the internal state of the game. */
+ TRANSPORT_RAIL = 0, //!< Tile with rail.
+ TRANSPORT_ROAD = 1, //!< Tile with road.
+ TRANSPORT_WATER = 2, //!< Tile with navigable waterways.
+ TRANSPORT_AIR = 3, //!< Tile with airport.
+
+ INVALID_TRANSPORT = -1, //!< Tile without any transport type.
+ };
+
+ /**
+ * Check if this tile is buildable, i.e. no things on it that needs
+ * demolishing.
+ * @param tile The tile to check on.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if it is buildable, false if not.
+ * @note For trams you also might want to check for AIRoad::IsRoad(),
+ * as you can build tram-rails on road-tiles.
+ * @note For rail you also might want to check for AIRoad::IsRoad(),
+ * as in some cases you can build rails on road-tiles.
+ */
+ static bool IsBuildable(TileIndex tile);
+
+ /**
+ * Check if this tile is buildable in a rectangle around a tile, with the
+ * entry in the list as top-left.
+ * @param tile The tile to check on.
+ * @param width The width of the rectangle.
+ * @param height The height of the rectangle.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if it is buildable, false if not.
+ */
+ static bool IsBuildableRectangle(TileIndex tile, uint width, uint height);
+
+ /**
+ * Checks whether the given tile is actually a water tile.
+ * @param tile The tile to check on.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if and only if the tile is a water tile.
+ */
+ static bool IsWaterTile(TileIndex tile);
+
+ /**
+ * Checks whether the given tile is actually a coast tile.
+ * @param tile The tile to check.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if and only if the tile is a coast tile.
+ * @note Building on coast tiles in general is more expensive.
+ */
+ static bool IsCoastTile(TileIndex tile);
+
+ /**
+ * Checks whether the given tile is a station tile of any station.
+ * @param tile The tile to check.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if and only if the tile is a station tile.
+ */
+ static bool IsStationTile(TileIndex tile);
+
+ /**
+ * Check if a tile has a steep slope.
+ * @param slope The slope to check on.
+ * @pre slope != SLOPE_INVALID.
+ * @return True if the slope is a steep slope.
+ */
+ static bool IsSteepSlope(Slope slope);
+
+ /**
+ * Check if a tile has a halftile slope.
+ * @param slope The slope to check on.
+ * @pre slope != SLOPE_INVALID.
+ * @return True if the slope is a halftile slope.
+ */
+ static bool IsHalftileSlope(Slope slope);
+
+ /**
+ * Check if the tile has any tree on it.
+ * @param tile The tile to check on.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if and only if there is a tree on the tile.
+ */
+ static bool HasTreeOnTile(TileIndex tile);
+
+ /**
+ * Check if the tile is a farmland tile.
+ * @param tile The tile to check on.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if and only if the tile is farmland.
+ */
+ static bool IsFarmTile(TileIndex tile);
+
+ /**
+ * Check if the tile is a rock tile.
+ * @param tile The tile to check on.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if and only if the tile is rock tile.
+ */
+ static bool IsRockTile(TileIndex tile);
+
+ /**
+ * Check if the tile is a rough tile.
+ * @param tile The tile to check on.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if and only if the tile is rough tile.
+ */
+ static bool IsRoughTile(TileIndex tile);
+
+ /**
+ * Check if the tile is a snow tile.
+ * @param tile The tile to check on.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if and only if the tile is snow tile.
+ */
+ static bool IsSnowTile(TileIndex tile);
+
+ /**
+ * Check if the tile is a desert tile.
+ * @param tile The tile to check on.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if and only if the tile is desert tile.
+ */
+ static bool IsDesertTile(TileIndex tile);
+
+ /**
+ * Get the slope of a tile.
+ * @param tile The tile to check on.
+ * @pre AIMap::IsValidTile(tile).
+ * @return 0 means flat, others indicate internal state of slope.
+ */
+ static Slope GetSlope(TileIndex tile);
+
+ /**
+ * Get the complement of the slope.
+ * @param slope The slope to get the complement of.
+ * @pre slope != SLOPE_INVALID.
+ * @pre !IsSteepSlope(slope).
+ * @pre !IsHalftileSlope(slope).
+ * @return The complement of a slope. This means that all corners that
+ * weren't raised, are raised, and visa versa.
+ */
+ static Slope GetComplementSlope(Slope slope);
+
+ /**
+ * Get the height of the tile.
+ * @param tile The tile to check on.
+ * @pre AIMap::IsValidTile(tile).
+ * @return The height of the tile, ranging from 0 to 15.
+ */
+ static int32 GetHeight(TileIndex tile);
+
+ /**
+ * Get the owner of the tile.
+ * @param tile The tile to get the owner from.
+ * @pre AIMap::IsValidTile(tile).
+ * @return The CompanyID of the owner of the tile, or INVALID_COMPANY if
+ * there is no owner (grass/industry/water tiles, etc.).
+ */
+ static AICompany::CompanyID GetOwner(TileIndex tile);
+
+ /**
+ * Checks whether the given tile contains parts suitable for the given
+ * TransportType.
+ * @param tile The tile to check.
+ * @param transport_type The TransportType to check against.
+ * @pre AIMap::IsValidTile(tile).
+ * @note Returns false on tiles with roadworks and on road tiles with only
+ * a single piece of road as these tiles cannot be used to transport
+ * anything on. It furthermore returns true on some coast tile for
+ * TRANSPORT_WATER because ships can navigate over them.
+ * @return True if and only if the tile has the given TransportType.
+ */
+ static bool HasTransportType(TileIndex tile, TransportType transport_type);
+
+ /**
+ * Check how much cargo this tile accepts.
+ * It creates a radius around the tile, and adds up all acceptance of this
+ * cargo.
+ * @param tile The tile to check on.
+ * @param cargo_type The cargo to check the acceptance of.
+ * @param width The width of the station.
+ * @param height The height of the station.
+ * @param radius The radius of the station.
+ * @pre AIMap::IsValidTile(tile).
+ * @return Value below 8 means no acceptance; the more the better.
+ */
+ static int32 GetCargoAcceptance(TileIndex tile, CargoID cargo_type, uint width, uint height, uint radius);
+
+ /**
+ * Checks how many tiles in the radius produces this cargo.
+ * It creates a radius around the tile, and adds up all tiles that produce
+ * this cargo.
+ * @param tile The tile to check on.
+ * @param cargo_type The cargo to check the production of.
+ * @param width The width of the station.
+ * @param height The height of the station.
+ * @param radius The radius of the station.
+ * @pre AIMap::IsValidTile(tile).
+ * @return The tiles that produce this cargo within radius of the tile.
+ * @note Town(houses) are not included in the value.
+ */
+ static int32 GetCargoProduction(TileIndex tile, CargoID cargo_type, uint width, uint height, uint radius);
+
+ /**
+ * Get the manhattan distance from the tile to the tile.
+ * @param tile_from The tile to get the distance to.
+ * @param tile_to The tile to get the distance to.
+ * @return The distance between the two tiles.
+ */
+ static int32 GetDistanceManhattanToTile(TileIndex tile_from, TileIndex tile_to);
+
+ /**
+ * Get the square distance from the tile to the tile.
+ * @param tile_from The tile to get the distance to.
+ * @param tile_to The tile to get the distance to.
+ * @return The distance between the two tiles.
+ */
+ static int32 GetDistanceSquareToTile(TileIndex tile_from, TileIndex tile_to);
+
+ /**
+ * Raise the given corners of the tile. The corners can be combined,
+ * for example: SLOPE_N | SLOPE_W (= SLOPE_NW)
+ * @param tile The tile to raise.
+ * @param slope Corners to raise (SLOPE_xxx).
+ * @pre AIMap::IsValidTile(tile).
+ * @exception AIError::ERR_AREA_NOT_CLEAR
+ * @exception AIError::ERR_TOO_CLOSE_TO_EDGE
+ * @exception AITile::ERR_TILE_TOO_HIGH
+ * @return 0 means failed, 1 means success.
+ */
+ static bool RaiseTile(TileIndex tile, int32 slope);
+
+ /**
+ * Lower the given corners of the tile. The corners can be combined,
+ * for example: SLOPE_N | SLOPE_W (= SLOPE_NW)
+ * @param tile The tile to lower.
+ * @param slope Corners to lower (SLOPE_xxx).
+ * @pre AIMap::IsValidTile(tile).
+ * @exception AIError::ERR_AREA_NOT_CLEAR
+ * @exception AIError::ERR_TOO_CLOSE_TO_EDGE
+ * @exception AITile::ERR_TILE_TOO_LOW
+ * @return 0 means failed, 1 means success.
+ */
+ static bool LowerTile(TileIndex tile, int32 slope);
+
+ /**
+ * Destroy everything on the given tile.
+ * @param tile The tile to demolish.
+ * @pre AIMap::IsValidTile(tile).
+ * @exception AIError::ERR_AREA_NOT_CLEAR
+ * @return True if and only if the tile was demolished.
+ */
+ static bool DemolishTile(TileIndex tile);
+
+ /**
+ * Create a random tree on a tile.
+ * @param tile The tile to build a tree on.
+ * @pre AIMap::IsValidTile(tile).
+ * @return True if and only if a tree was added on the tile.
+ */
+ static bool PlantTree(TileIndex tile);
+
+ /**
+ * Create a random tree on a rectangle of tiles.
+ * @param tile The top left tile of the rectangle.
+ * @param width The width of the rectangle.
+ * @param height The height of the rectangle.
+ * @pre AIMap::IsValidTile(tile).
+ * @pre width >= 1 && width <= 20.
+ * @pre height >= 1 && height <= 20.
+ * @return True if and only if a tree was added on any of the tiles in the rectangle.
+ */
+ static bool PlantTreeRectangle(TileIndex tile, uint width, uint height);
+
+ /**
+ * Find out if this tile is within the rating influence of a town.
+ * Stations on this tile influence the rating of the town.
+ * @param tile The tile to check.
+ * @param town_id The town to check.
+ * @return True if the tile is within the rating influence of the town.
+ */
+ static bool IsWithinTownInfluence(TileIndex tile, TownID town_id);
+
+ /**
+ * Find the town that is closest to a tile. Stations you build at this tile
+ * will belong to this town.
+ * @param tile The tile to check.
+ * @return The TownID of the town closest to the tile.
+ */
+ static TownID GetClosestTown(TileIndex tile);
+};
+
+#endif /* AI_TILE_HPP */
diff --git a/src/ai/api/ai_tile.hpp.sq b/src/ai/api/ai_tile.hpp.sq
new file mode 100644
index 000000000..920dc6543
--- /dev/null
+++ b/src/ai/api/ai_tile.hpp.sq
@@ -0,0 +1,97 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_tile.hpp"
+
+namespace SQConvert {
+ /* Allow enums to be used as Squirrel parameters */
+ template <> AITile::ErrorMessages GetParam(ForceType<AITile::ErrorMessages>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AITile::ErrorMessages)tmp; }
+ template <> int Return<AITile::ErrorMessages>(HSQUIRRELVM vm, AITile::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; }
+ template <> AITile::Slope GetParam(ForceType<AITile::Slope>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AITile::Slope)tmp; }
+ template <> int Return<AITile::Slope>(HSQUIRRELVM vm, AITile::Slope res) { sq_pushinteger(vm, (int32)res); return 1; }
+ template <> AITile::TransportType GetParam(ForceType<AITile::TransportType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AITile::TransportType)tmp; }
+ template <> int Return<AITile::TransportType>(HSQUIRRELVM vm, AITile::TransportType res) { sq_pushinteger(vm, (int32)res); return 1; }
+
+ /* Allow AITile to be used as Squirrel parameter */
+ template <> AITile *GetParam(ForceType<AITile *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITile *)instance; }
+ template <> AITile &GetParam(ForceType<AITile &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITile *)instance; }
+ template <> const AITile *GetParam(ForceType<const AITile *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITile *)instance; }
+ template <> const AITile &GetParam(ForceType<const AITile &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITile *)instance; }
+ template <> int Return<AITile *>(HSQUIRRELVM vm, AITile *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AITile", res, NULL, DefSQDestructorCallback<AITile>); return 1; }
+}; // namespace SQConvert
+
+void SQAITile_Register(Squirrel *engine) {
+ DefSQClass <AITile> SQAITile("AITile");
+ SQAITile.PreRegister(engine);
+ SQAITile.AddConstructor<void (AITile::*)(), 1>(engine, "x");
+
+ SQAITile.DefSQConst(engine, AITile::ERR_TILE_BASE, "ERR_TILE_BASE");
+ SQAITile.DefSQConst(engine, AITile::ERR_TILE_TOO_HIGH, "ERR_TILE_TOO_HIGH");
+ SQAITile.DefSQConst(engine, AITile::ERR_TILE_TOO_LOW, "ERR_TILE_TOO_LOW");
+ SQAITile.DefSQConst(engine, AITile::SLOPE_FLAT, "SLOPE_FLAT");
+ SQAITile.DefSQConst(engine, AITile::SLOPE_W, "SLOPE_W");
+ SQAITile.DefSQConst(engine, AITile::SLOPE_S, "SLOPE_S");
+ SQAITile.DefSQConst(engine, AITile::SLOPE_E, "SLOPE_E");
+ SQAITile.DefSQConst(engine, AITile::SLOPE_N, "SLOPE_N");
+ SQAITile.DefSQConst(engine, AITile::SLOPE_STEEP, "SLOPE_STEEP");
+ SQAITile.DefSQConst(engine, AITile::SLOPE_NW, "SLOPE_NW");
+ SQAITile.DefSQConst(engine, AITile::SLOPE_SW, "SLOPE_SW");
+ SQAITile.DefSQConst(engine, AITile::SLOPE_SE, "SLOPE_SE");
+ SQAITile.DefSQConst(engine, AITile::SLOPE_NE, "SLOPE_NE");
+ SQAITile.DefSQConst(engine, AITile::SLOPE_EW, "SLOPE_EW");
+ SQAITile.DefSQConst(engine, AITile::SLOPE_NS, "SLOPE_NS");
+ SQAITile.DefSQConst(engine, AITile::SLOPE_ELEVATED, "SLOPE_ELEVATED");
+ SQAITile.DefSQConst(engine, AITile::SLOPE_NWS, "SLOPE_NWS");
+ SQAITile.DefSQConst(engine, AITile::SLOPE_WSE, "SLOPE_WSE");
+ SQAITile.DefSQConst(engine, AITile::SLOPE_SEN, "SLOPE_SEN");
+ SQAITile.DefSQConst(engine, AITile::SLOPE_ENW, "SLOPE_ENW");
+ SQAITile.DefSQConst(engine, AITile::SLOPE_STEEP_W, "SLOPE_STEEP_W");
+ SQAITile.DefSQConst(engine, AITile::SLOPE_STEEP_S, "SLOPE_STEEP_S");
+ SQAITile.DefSQConst(engine, AITile::SLOPE_STEEP_E, "SLOPE_STEEP_E");
+ SQAITile.DefSQConst(engine, AITile::SLOPE_STEEP_N, "SLOPE_STEEP_N");
+ SQAITile.DefSQConst(engine, AITile::SLOPE_INVALID, "SLOPE_INVALID");
+ SQAITile.DefSQConst(engine, AITile::TRANSPORT_RAIL, "TRANSPORT_RAIL");
+ SQAITile.DefSQConst(engine, AITile::TRANSPORT_ROAD, "TRANSPORT_ROAD");
+ SQAITile.DefSQConst(engine, AITile::TRANSPORT_WATER, "TRANSPORT_WATER");
+ SQAITile.DefSQConst(engine, AITile::TRANSPORT_AIR, "TRANSPORT_AIR");
+ SQAITile.DefSQConst(engine, AITile::INVALID_TRANSPORT, "INVALID_TRANSPORT");
+
+ AIError::RegisterErrorMap(STR_1003_ALREADY_AT_SEA_LEVEL, AITile::ERR_TILE_TOO_HIGH);
+ AIError::RegisterErrorMap(STR_1003_ALREADY_AT_SEA_LEVEL, AITile::ERR_TILE_TOO_LOW);
+
+ AIError::RegisterErrorMapString(AITile::ERR_TILE_TOO_HIGH, "ERR_TILE_TOO_HIGH");
+ AIError::RegisterErrorMapString(AITile::ERR_TILE_TOO_LOW, "ERR_TILE_TOO_LOW");
+
+ SQAITile.DefSQStaticMethod(engine, &AITile::GetClassName, "GetClassName", 1, "x");
+ SQAITile.DefSQStaticMethod(engine, &AITile::IsBuildable, "IsBuildable", 2, "xi");
+ SQAITile.DefSQStaticMethod(engine, &AITile::IsBuildableRectangle, "IsBuildableRectangle", 4, "xiii");
+ SQAITile.DefSQStaticMethod(engine, &AITile::IsWaterTile, "IsWaterTile", 2, "xi");
+ SQAITile.DefSQStaticMethod(engine, &AITile::IsCoastTile, "IsCoastTile", 2, "xi");
+ SQAITile.DefSQStaticMethod(engine, &AITile::IsStationTile, "IsStationTile", 2, "xi");
+ SQAITile.DefSQStaticMethod(engine, &AITile::IsSteepSlope, "IsSteepSlope", 2, "xi");
+ SQAITile.DefSQStaticMethod(engine, &AITile::IsHalftileSlope, "IsHalftileSlope", 2, "xi");
+ SQAITile.DefSQStaticMethod(engine, &AITile::HasTreeOnTile, "HasTreeOnTile", 2, "xi");
+ SQAITile.DefSQStaticMethod(engine, &AITile::IsFarmTile, "IsFarmTile", 2, "xi");
+ SQAITile.DefSQStaticMethod(engine, &AITile::IsRockTile, "IsRockTile", 2, "xi");
+ SQAITile.DefSQStaticMethod(engine, &AITile::IsRoughTile, "IsRoughTile", 2, "xi");
+ SQAITile.DefSQStaticMethod(engine, &AITile::IsSnowTile, "IsSnowTile", 2, "xi");
+ SQAITile.DefSQStaticMethod(engine, &AITile::IsDesertTile, "IsDesertTile", 2, "xi");
+ SQAITile.DefSQStaticMethod(engine, &AITile::GetSlope, "GetSlope", 2, "xi");
+ SQAITile.DefSQStaticMethod(engine, &AITile::GetComplementSlope, "GetComplementSlope", 2, "xi");
+ SQAITile.DefSQStaticMethod(engine, &AITile::GetHeight, "GetHeight", 2, "xi");
+ SQAITile.DefSQStaticMethod(engine, &AITile::GetOwner, "GetOwner", 2, "xi");
+ SQAITile.DefSQStaticMethod(engine, &AITile::HasTransportType, "HasTransportType", 3, "xii");
+ SQAITile.DefSQStaticMethod(engine, &AITile::GetCargoAcceptance, "GetCargoAcceptance", 6, "xiiiii");
+ SQAITile.DefSQStaticMethod(engine, &AITile::GetCargoProduction, "GetCargoProduction", 6, "xiiiii");
+ SQAITile.DefSQStaticMethod(engine, &AITile::GetDistanceManhattanToTile, "GetDistanceManhattanToTile", 3, "xii");
+ SQAITile.DefSQStaticMethod(engine, &AITile::GetDistanceSquareToTile, "GetDistanceSquareToTile", 3, "xii");
+ SQAITile.DefSQStaticMethod(engine, &AITile::RaiseTile, "RaiseTile", 3, "xii");
+ SQAITile.DefSQStaticMethod(engine, &AITile::LowerTile, "LowerTile", 3, "xii");
+ SQAITile.DefSQStaticMethod(engine, &AITile::DemolishTile, "DemolishTile", 2, "xi");
+ SQAITile.DefSQStaticMethod(engine, &AITile::PlantTree, "PlantTree", 2, "xi");
+ SQAITile.DefSQStaticMethod(engine, &AITile::PlantTreeRectangle, "PlantTreeRectangle", 4, "xiii");
+ SQAITile.DefSQStaticMethod(engine, &AITile::IsWithinTownInfluence, "IsWithinTownInfluence", 3, "xii");
+ SQAITile.DefSQStaticMethod(engine, &AITile::GetClosestTown, "GetClosestTown", 2, "xi");
+
+ SQAITile.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_tilelist.cpp b/src/ai/api/ai_tilelist.cpp
new file mode 100644
index 000000000..24575c697
--- /dev/null
+++ b/src/ai/api/ai_tilelist.cpp
@@ -0,0 +1,175 @@
+/* $Id$ */
+
+/** @file ai_tilelist.cpp Implementation of AITileList and friends. */
+
+#include "ai_tilelist.hpp"
+#include "ai_industry.hpp"
+#include "../../openttd.h"
+#include "../../landscape.h"
+#include "../../settings_type.h"
+#include "../../station_func.h"
+#include "../../map_func.h"
+#include "../../tile_map.h"
+#include "../../industry_map.h"
+#include "../../station_base.h"
+#include "../../station_map.h"
+
+void AITileList::FixRectangleSpan(TileIndex &t1, TileIndex &t2)
+{
+ uint x1 = ::TileX(t1);
+ uint x2 = ::TileX(t2);
+
+ uint y1 = ::TileY(t1);
+ uint y2 = ::TileY(t2);
+
+ if (x1 >= x2) ::Swap(x1, x2);
+ if (y1 >= y2) ::Swap(y1, y2);
+
+ t1 = ::TileXY(x1, y1);
+ t2 = ::TileXY(x2, y2);
+}
+
+void AITileList::AddRectangle(TileIndex t1, TileIndex t2)
+{
+ if (!::IsValidTile(t1)) return;
+ if (!::IsValidTile(t2)) return;
+
+ this->FixRectangleSpan(t1, t2);
+
+ uint w = TileX(t2) - TileX(t1) + 1;
+ uint h = TileY(t2) - TileY(t1) + 1;
+
+ BEGIN_TILE_LOOP(t, w, h, t1) {
+ this->AddItem(t);
+ } END_TILE_LOOP(t, w, h, t1)
+}
+
+void AITileList::AddTile(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return;
+
+ this->AddItem(tile);
+}
+
+void AITileList::RemoveRectangle(TileIndex t1, TileIndex t2)
+{
+ if (!::IsValidTile(t1)) return;
+ if (!::IsValidTile(t2)) return;
+
+ this->FixRectangleSpan(t1, t2);
+
+ uint w = TileX(t2) - TileX(t1) + 1;
+ uint h = TileY(t2) - TileY(t1) + 1;
+
+ BEGIN_TILE_LOOP(t, w, h, t1) {
+ this->RemoveItem(t);
+ } END_TILE_LOOP(t, w, h, t1)
+}
+
+void AITileList::RemoveTile(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return;
+
+ this->RemoveItem(tile);
+}
+
+AITileList_IndustryAccepting::AITileList_IndustryAccepting(IndustryID industry_id, uint radius)
+{
+ if (!AIIndustry::IsValidIndustry(industry_id)) return;
+
+ const Industry *i = ::GetIndustry(industry_id);
+ const IndustrySpec *indsp = ::GetIndustrySpec(i->type);
+
+ /* Check if this industry accepts anything */
+ {
+ bool cargo_accepts = false;
+ for (byte j = 0; j < lengthof(indsp->accepts_cargo); j++) {
+ if (indsp->accepts_cargo[j] != CT_INVALID) cargo_accepts = true;
+ }
+ if (!cargo_accepts) return;
+ }
+
+ if (!_settings_game.station.modified_catchment) radius = CA_UNMODIFIED;
+
+ BEGIN_TILE_LOOP(cur_tile, i->width + radius * 2, i->height + radius * 2, i->xy - ::TileDiffXY(radius, radius)) {
+ if (!::IsValidTile(cur_tile)) continue;
+ /* Exclude all tiles that belong to this industry */
+ if (::IsTileType(cur_tile, MP_INDUSTRY) && ::GetIndustryIndex(cur_tile) == industry_id) continue;
+
+ /* Only add the tile if it accepts the cargo (sometimes just 1 tile of an
+ * industry triggers the acceptance). */
+ AcceptedCargo accepts;
+ ::GetAcceptanceAroundTiles(accepts, cur_tile, 1, 1, radius);
+ {
+ bool cargo_accepts = false;
+ for (byte j = 0; j < lengthof(indsp->accepts_cargo); j++) {
+ if (indsp->accepts_cargo[j] != CT_INVALID && accepts[indsp->accepts_cargo[j]] != 0) cargo_accepts = true;
+ }
+ if (!cargo_accepts) continue;
+ }
+
+ this->AddTile(cur_tile);
+ } END_TILE_LOOP(cur_tile, i->width + radius * 2, i->height + radius * 2, i->xy - ::TileDiffXY(radius, radius))
+}
+
+AITileList_IndustryProducing::AITileList_IndustryProducing(IndustryID industry_id, uint radius)
+{
+ if (!AIIndustry::IsValidIndustry(industry_id)) return;
+
+ const Industry *i = ::GetIndustry(industry_id);
+ const IndustrySpec *indsp = ::GetIndustrySpec(i->type);
+
+ /* Check if this industry produces anything */
+ {
+ bool cargo_produces = false;
+ for (byte j = 0; j < lengthof(indsp->produced_cargo); j++) {
+ if (indsp->produced_cargo[j] != CT_INVALID) cargo_produces = true;
+ }
+ if (!cargo_produces) return;
+ }
+
+ if (!_settings_game.station.modified_catchment) radius = CA_UNMODIFIED;
+
+ BEGIN_TILE_LOOP(cur_tile, i->width + radius * 2, i->height + radius * 2, i->xy - ::TileDiffXY(radius, radius)) {
+ if (!::IsValidTile(cur_tile)) continue;
+ /* Exclude all tiles that belong to this industry */
+ if (::IsTileType(cur_tile, MP_INDUSTRY) && ::GetIndustryIndex(cur_tile) == industry_id) continue;
+
+ /* Only add the tile if it produces the cargo (a bug in OpenTTD makes this
+ * inconsitance). */
+ AcceptedCargo produces;
+ ::GetProductionAroundTiles(produces, cur_tile, 1, 1, radius);
+ {
+ bool cargo_produces = false;
+ for (byte j = 0; j < lengthof(indsp->produced_cargo); j++) {
+ if (indsp->produced_cargo[j] != CT_INVALID && produces[indsp->produced_cargo[j]] != 0) cargo_produces = true;
+ }
+ if (!cargo_produces) continue;
+ }
+
+ this->AddTile(cur_tile);
+ } END_TILE_LOOP(cur_tile, i->width + radius * 2, i->height + radius * 2, i->xy - ::TileDiffXY(radius, radius))
+}
+
+AITileList_StationType::AITileList_StationType(StationID station_id, AIStation::StationType station_type)
+{
+ if (!AIStation::IsValidStation(station_id)) return;
+
+ const StationRect *rect = &::GetStation(station_id)->rect;
+
+ uint station_type_value = 0;
+ /* Convert AIStation::StationType to ::StationType, but do it in a
+ * bitmask, so we can scan for multiple entries at the same time. */
+ if ((station_type & AIStation::STATION_TRAIN) != 0) station_type_value |= (1 << ::STATION_RAIL);
+ if ((station_type & AIStation::STATION_TRUCK_STOP) != 0) station_type_value |= (1 << ::STATION_TRUCK);
+ if ((station_type & AIStation::STATION_BUS_STOP) != 0) station_type_value |= (1 << ::STATION_BUS);
+ if ((station_type & AIStation::STATION_AIRPORT) != 0) station_type_value |= (1 << ::STATION_AIRPORT) | (1 << ::STATION_OILRIG);
+ if ((station_type & AIStation::STATION_DOCK) != 0) station_type_value |= (1 << ::STATION_DOCK) | (1 << ::STATION_OILRIG);
+
+ BEGIN_TILE_LOOP(cur_tile, rect->right - rect->left + 1, rect->bottom - rect->top + 1, ::TileXY(rect->left, rect->top)) {
+ if (!::IsTileType(cur_tile, MP_STATION)) continue;
+ if (::GetStationIndex(cur_tile) != station_id) continue;
+ if (!HasBit(station_type_value, ::GetStationType(cur_tile))) continue;
+ this->AddTile(cur_tile);
+ } END_TILE_LOOP(cur_tile, rect->right - rect->left + 1, rect->bottom - rect->top + 1, ::TileXY(rect->left, rect->top))
+}
diff --git a/src/ai/api/ai_tilelist.hpp b/src/ai/api/ai_tilelist.hpp
new file mode 100644
index 000000000..f3d2f658d
--- /dev/null
+++ b/src/ai/api/ai_tilelist.hpp
@@ -0,0 +1,110 @@
+/* $Id$ */
+
+/** @file ai_tilelist.hpp List tiles. */
+
+#ifndef AI_TILELIST_HPP
+#define AI_TILELIST_HPP
+
+#include "ai_abstractlist.hpp"
+#include "ai_station.hpp"
+
+/**
+ * Creates an empty list, in which you can add tiles.
+ * @ingroup AIList
+ */
+class AITileList : public AIAbstractList {
+public:
+ static const char *GetClassName() { return "AITileList"; }
+
+private:
+ /**
+ * Make sure t1.x is smaller than t2.x and t1.y is smaller than t2.y.
+ * They are swapped to ensure they are after calling this function.
+ * @param t1 one of the corners of the rectangle.
+ * @param t2 the other corner of the rectangle.
+ */
+ void FixRectangleSpan(TileIndex &t1, TileIndex &t2);
+
+public:
+ /**
+ * Adds the rectangle between tile_from and tile_to to the to-be-evaluated tiles.
+ * @param tile_from One corner of the tiles to add.
+ * @param tile_to The other corner of the tiles to add.
+ * @pre AIMap::IsValidTile(tile_from).
+ * @pre AIMap::IsValidTile(tile_to).
+ */
+ void AddRectangle(TileIndex tile_from, TileIndex tile_to);
+
+ /**
+ * Add a tile to the to-be-evaluated tiles.
+ * @param tile The tile to add.
+ * @pre AIMap::IsValidTile(tile).
+ */
+ void AddTile(TileIndex tile);
+
+ /**
+ * Remove the tiles inside the rectangle between tile_from and tile_to form the list.
+ * @param tile_from One corner of the tiles to remove.
+ * @param tile_to The other corner of the files to remove.
+ * @pre AIMap::IsValidTile(tile_from).
+ * @pre AIMap::IsValidTile(tile_to).
+ */
+ void RemoveRectangle(TileIndex tile_from, TileIndex tile_to);
+
+ /**
+ * Remove a tile from the list.
+ * @param tile The tile to remove.
+ * @pre AIMap::IsValidTile(tile).
+ */
+ void RemoveTile(TileIndex tile);
+};
+
+/**
+ * Creates a list of tiles that will accept cargo for the given industry.
+ * @note If a simular industry is close, it might happen that this industry receives the cargo.
+ * @ingroup AIList
+ */
+class AITileList_IndustryAccepting : public AITileList {
+public:
+ static const char *GetClassName() { return "AITileList_IndustryAccepting"; }
+
+ /**
+ * @param industry_id The industry to create the AITileList around.
+ * @param radius The radius of the station you will be using.
+ */
+ AITileList_IndustryAccepting(IndustryID industry_id, uint radius);
+};
+
+/**
+ * Creates a list of tiles which the industry checks to see if a station is
+ * there to receive cargo produced by this industry.
+ * @ingroup AIList
+ */
+class AITileList_IndustryProducing : public AITileList {
+public:
+ static const char *GetClassName() { return "AITileList_IndustryProducing"; }
+
+ /**
+ * @param industry_id The industry to create the AITileList around.
+ * @param radius The radius of the station you will be using.
+ */
+ AITileList_IndustryProducing(IndustryID industry_id, uint radius);
+};
+
+/**
+ * Creates a list of tiles which have the requested StationType of the
+ * StationID.
+ * @ingroup AIList
+ */
+class AITileList_StationType : public AITileList {
+public:
+ static const char *GetClassName() { return "AITileList_StationType"; }
+
+ /**
+ * @param station_id The station to create the AITileList for.
+ * @param station_type The StationType to create the AIList for.
+ */
+ AITileList_StationType(StationID station_id, AIStation::StationType station_type);
+};
+
+#endif /* AI_TILELIST_HPP */
diff --git a/src/ai/api/ai_tilelist.hpp.sq b/src/ai/api/ai_tilelist.hpp.sq
new file mode 100644
index 000000000..4e6bfdf30
--- /dev/null
+++ b/src/ai/api/ai_tilelist.hpp.sq
@@ -0,0 +1,85 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_tilelist.hpp"
+
+namespace SQConvert {
+ /* Allow AITileList to be used as Squirrel parameter */
+ template <> AITileList *GetParam(ForceType<AITileList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITileList *)instance; }
+ template <> AITileList &GetParam(ForceType<AITileList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITileList *)instance; }
+ template <> const AITileList *GetParam(ForceType<const AITileList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITileList *)instance; }
+ template <> const AITileList &GetParam(ForceType<const AITileList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITileList *)instance; }
+ template <> int Return<AITileList *>(HSQUIRRELVM vm, AITileList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AITileList", res, NULL, DefSQDestructorCallback<AITileList>); return 1; }
+}; // namespace SQConvert
+
+void SQAITileList_Register(Squirrel *engine) {
+ DefSQClass <AITileList> SQAITileList("AITileList");
+ SQAITileList.PreRegister(engine, "AIAbstractList");
+ SQAITileList.AddConstructor<void (AITileList::*)(), 1>(engine, "x");
+
+ SQAITileList.DefSQStaticMethod(engine, &AITileList::GetClassName, "GetClassName", 1, "x");
+
+ SQAITileList.DefSQMethod(engine, &AITileList::AddRectangle, "AddRectangle", 3, "xii");
+ SQAITileList.DefSQMethod(engine, &AITileList::AddTile, "AddTile", 2, "xi");
+ SQAITileList.DefSQMethod(engine, &AITileList::RemoveRectangle, "RemoveRectangle", 3, "xii");
+ SQAITileList.DefSQMethod(engine, &AITileList::RemoveTile, "RemoveTile", 2, "xi");
+
+ SQAITileList.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AITileList_IndustryAccepting to be used as Squirrel parameter */
+ template <> AITileList_IndustryAccepting *GetParam(ForceType<AITileList_IndustryAccepting *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITileList_IndustryAccepting *)instance; }
+ template <> AITileList_IndustryAccepting &GetParam(ForceType<AITileList_IndustryAccepting &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITileList_IndustryAccepting *)instance; }
+ template <> const AITileList_IndustryAccepting *GetParam(ForceType<const AITileList_IndustryAccepting *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITileList_IndustryAccepting *)instance; }
+ template <> const AITileList_IndustryAccepting &GetParam(ForceType<const AITileList_IndustryAccepting &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITileList_IndustryAccepting *)instance; }
+ template <> int Return<AITileList_IndustryAccepting *>(HSQUIRRELVM vm, AITileList_IndustryAccepting *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AITileList_IndustryAccepting", res, NULL, DefSQDestructorCallback<AITileList_IndustryAccepting>); return 1; }
+}; // namespace SQConvert
+
+void SQAITileList_IndustryAccepting_Register(Squirrel *engine) {
+ DefSQClass <AITileList_IndustryAccepting> SQAITileList_IndustryAccepting("AITileList_IndustryAccepting");
+ SQAITileList_IndustryAccepting.PreRegister(engine, "AITileList");
+ SQAITileList_IndustryAccepting.AddConstructor<void (AITileList_IndustryAccepting::*)(IndustryID industry_id, uint radius), 3>(engine, "xii");
+
+ SQAITileList_IndustryAccepting.DefSQStaticMethod(engine, &AITileList_IndustryAccepting::GetClassName, "GetClassName", 1, "x");
+
+ SQAITileList_IndustryAccepting.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AITileList_IndustryProducing to be used as Squirrel parameter */
+ template <> AITileList_IndustryProducing *GetParam(ForceType<AITileList_IndustryProducing *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITileList_IndustryProducing *)instance; }
+ template <> AITileList_IndustryProducing &GetParam(ForceType<AITileList_IndustryProducing &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITileList_IndustryProducing *)instance; }
+ template <> const AITileList_IndustryProducing *GetParam(ForceType<const AITileList_IndustryProducing *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITileList_IndustryProducing *)instance; }
+ template <> const AITileList_IndustryProducing &GetParam(ForceType<const AITileList_IndustryProducing &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITileList_IndustryProducing *)instance; }
+ template <> int Return<AITileList_IndustryProducing *>(HSQUIRRELVM vm, AITileList_IndustryProducing *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AITileList_IndustryProducing", res, NULL, DefSQDestructorCallback<AITileList_IndustryProducing>); return 1; }
+}; // namespace SQConvert
+
+void SQAITileList_IndustryProducing_Register(Squirrel *engine) {
+ DefSQClass <AITileList_IndustryProducing> SQAITileList_IndustryProducing("AITileList_IndustryProducing");
+ SQAITileList_IndustryProducing.PreRegister(engine, "AITileList");
+ SQAITileList_IndustryProducing.AddConstructor<void (AITileList_IndustryProducing::*)(IndustryID industry_id, uint radius), 3>(engine, "xii");
+
+ SQAITileList_IndustryProducing.DefSQStaticMethod(engine, &AITileList_IndustryProducing::GetClassName, "GetClassName", 1, "x");
+
+ SQAITileList_IndustryProducing.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AITileList_StationType to be used as Squirrel parameter */
+ template <> AITileList_StationType *GetParam(ForceType<AITileList_StationType *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITileList_StationType *)instance; }
+ template <> AITileList_StationType &GetParam(ForceType<AITileList_StationType &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITileList_StationType *)instance; }
+ template <> const AITileList_StationType *GetParam(ForceType<const AITileList_StationType *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITileList_StationType *)instance; }
+ template <> const AITileList_StationType &GetParam(ForceType<const AITileList_StationType &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITileList_StationType *)instance; }
+ template <> int Return<AITileList_StationType *>(HSQUIRRELVM vm, AITileList_StationType *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AITileList_StationType", res, NULL, DefSQDestructorCallback<AITileList_StationType>); return 1; }
+}; // namespace SQConvert
+
+void SQAITileList_StationType_Register(Squirrel *engine) {
+ DefSQClass <AITileList_StationType> SQAITileList_StationType("AITileList_StationType");
+ SQAITileList_StationType.PreRegister(engine, "AITileList");
+ SQAITileList_StationType.AddConstructor<void (AITileList_StationType::*)(StationID station_id, AIStation::StationType station_type), 3>(engine, "xii");
+
+ SQAITileList_StationType.DefSQStaticMethod(engine, &AITileList_StationType::GetClassName, "GetClassName", 1, "x");
+
+ SQAITileList_StationType.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_town.cpp b/src/ai/api/ai_town.cpp
new file mode 100644
index 000000000..8c8ab9c02
--- /dev/null
+++ b/src/ai/api/ai_town.cpp
@@ -0,0 +1,197 @@
+/* $Id$ */
+
+/** @file ai_town.cpp Implementation of AITown. */
+
+#include "ai_town.hpp"
+#include "ai_map.hpp"
+#include "ai_cargo.hpp"
+#include "ai_error.hpp"
+#include "../../command_type.h"
+#include "../../openttd.h"
+#include "../../town.h"
+#include "../../strings_func.h"
+#include "../../core/alloc_func.hpp"
+#include "../../company_func.h"
+#include "../../station_base.h"
+#include "table/strings.h"
+
+/* static */ TownID AITown::GetMaxTownID()
+{
+ return ::GetMaxTownIndex();
+}
+
+/* static */ int32 AITown::GetTownCount()
+{
+ return ::GetNumTowns();
+}
+
+/* static */ bool AITown::IsValidTown(TownID town_id)
+{
+ return ::IsValidTownID(town_id);
+}
+
+/* static */ const char *AITown::GetName(TownID town_id)
+{
+ if (!IsValidTown(town_id)) return NULL;
+ static const int len = 64;
+ char *town_name = MallocT<char>(len);
+
+ ::SetDParam(0, town_id);
+ ::GetString(town_name, STR_TOWN, &town_name[len - 1]);
+
+ return town_name;
+}
+
+/* static */ int32 AITown::GetPopulation(TownID town_id)
+{
+ if (!IsValidTown(town_id)) return -1;
+ const Town *t = ::GetTown(town_id);
+ return t->population;
+}
+
+/* static */ int32 AITown::GetHouseCount(TownID town_id)
+{
+ if (!IsValidTown(town_id)) return -1;
+ const Town *t = ::GetTown(town_id);
+ return t->num_houses;
+}
+
+/* static */ TileIndex AITown::GetLocation(TownID town_id)
+{
+ if (!IsValidTown(town_id)) return INVALID_TILE;
+ const Town *t = ::GetTown(town_id);
+ return t->xy;
+}
+
+/* static */ int32 AITown::GetLastMonthProduction(TownID town_id, CargoID cargo_id)
+{
+ if (!IsValidTown(town_id)) return -1;
+ if (!AICargo::IsValidCargo(cargo_id)) return -1;
+
+ const Town *t = ::GetTown(town_id);
+
+ switch(AICargo::GetTownEffect(cargo_id)) {
+ case AICargo::TE_PASSENGERS: return t->act_pass;
+ case AICargo::TE_MAIL: return t->act_mail;
+ default: return -1;
+ }
+}
+
+/* static */ int32 AITown::GetLastMonthTransported(TownID town_id, CargoID cargo_id)
+{
+ if (!IsValidTown(town_id)) return -1;
+ if (!AICargo::IsValidCargo(cargo_id)) return -1;
+
+ const Town *t = ::GetTown(town_id);
+
+ switch(AICargo::GetTownEffect(cargo_id)) {
+ case AICargo::TE_PASSENGERS: return t->pct_pass_transported;
+ case AICargo::TE_MAIL: return t->pct_mail_transported;
+ default: return -1;
+ }
+}
+
+/* static */ int32 AITown::GetMaxProduction(TownID town_id, CargoID cargo_id)
+{
+ if (!IsValidTown(town_id)) return -1;
+ if (!AICargo::IsValidCargo(cargo_id)) return -1;
+
+ const Town *t = ::GetTown(town_id);
+
+ switch(AICargo::GetTownEffect(cargo_id)) {
+ case AICargo::TE_PASSENGERS: return t->max_pass;
+ case AICargo::TE_MAIL: return t->max_mail;
+ default: return -1;
+ }
+}
+
+/* static */ int32 AITown::GetDistanceManhattanToTile(TownID town_id, TileIndex tile)
+{
+ return AIMap::DistanceManhattan(tile, GetLocation(town_id));
+}
+
+/* static */ int32 AITown::GetDistanceSquareToTile(TownID town_id, TileIndex tile)
+{
+ return AIMap::DistanceSquare(tile, GetLocation(town_id));
+}
+
+/* static */ bool AITown::IsWithinTownInfluence(TownID town_id, TileIndex tile)
+{
+ if (!IsValidTown(town_id)) return false;
+
+ const Town *t = ::GetTown(town_id);
+ return ((uint32)GetDistanceSquareToTile(town_id, tile) <= t->squared_town_zone_radius[0]);
+}
+
+/* static */ bool AITown::HasStatue(TownID town_id)
+{
+ if (!IsValidTown(town_id)) return false;
+
+ return ::HasBit(::GetTown(town_id)->statues, _current_company);
+}
+
+/* static */ int AITown::GetRoadReworkDuration(TownID town_id)
+{
+ if (!IsValidTown(town_id)) return -1;
+
+ return ::GetTown(town_id)->road_build_months;
+}
+
+/* static */ AICompany::CompanyID AITown::GetExclusiveRightsCompany(TownID town_id)
+{
+ if (!IsValidTown(town_id)) return AICompany::INVALID_COMPANY;
+
+ return (AICompany::CompanyID)(int8)::GetTown(town_id)->exclusivity;
+}
+
+/* static */ int32 AITown::GetExclusiveRightsDuration(TownID town_id)
+{
+ if (!IsValidTown(town_id)) return -1;
+
+ return ::GetTown(town_id)->exclusive_counter;
+}
+
+extern uint GetMaskOfTownActions(int *nump, CompanyID cid, const Town *t);
+
+/* static */ bool AITown::IsActionAvailable(TownID town_id, TownAction town_action)
+{
+ if (!IsValidTown(town_id)) return false;
+
+ return HasBit(::GetMaskOfTownActions(NULL, _current_company, ::GetTown(town_id)), town_action);
+}
+
+/* static */ bool AITown::PerformTownAction(TownID town_id, TownAction town_action)
+{
+ EnforcePrecondition(false, IsValidTown(town_id));
+ EnforcePrecondition(false, IsActionAvailable(town_id, town_action));
+
+ return AIObject::DoCommand(::GetTown(town_id)->xy, town_id, town_action, CMD_DO_TOWN_ACTION);
+}
+
+/* static */ AITown::TownRating AITown::GetRating(TownID town_id, AICompany::CompanyID company_id)
+{
+ if (!IsValidTown(town_id)) return INVALID_TOWN_RATING;
+ AICompany::CompanyID company = AICompany::ResolveCompanyID(company_id);
+ if (company == AICompany::INVALID_COMPANY) return INVALID_TOWN_RATING;
+
+ const Town *t = ::GetTown(town_id);
+ if (!HasBit(t->have_ratings, company)) return TOWN_RATING_NONE;
+ return max(TOWN_RATING_APPALLING, (TownRating)((t->ratings[company] / 200) + 3));
+}
+
+/* static */ int AITown::GetAllowedNoise(TownID town_id)
+{
+ if (!IsValidTown(town_id)) return -1;
+
+ const Town *t = ::GetTown(town_id);
+ if (_settings_game.economy.station_noise_level) {
+ return t->MaxTownNoise() - t->noise_reached;
+ }
+
+ int num = 0;
+ const Station *st;
+ FOR_ALL_STATIONS(st) {
+ if (st->town == t && st->facilities & FACIL_AIRPORT && st->airport_type != AT_OILRIG) num++;
+ }
+ return max(0, 2 - num);
+}
diff --git a/src/ai/api/ai_town.hpp b/src/ai/api/ai_town.hpp
new file mode 100644
index 000000000..94ba91e17
--- /dev/null
+++ b/src/ai/api/ai_town.hpp
@@ -0,0 +1,284 @@
+/* $Id$ */
+
+/** @file ai_town.hpp Everything to query towns. */
+
+#ifndef AI_TOWN_HPP
+#define AI_TOWN_HPP
+
+#include "ai_object.hpp"
+#include "ai_company.hpp"
+
+/**
+ * Class that handles all town related functions.
+ */
+class AITown : public AIObject {
+public:
+ static const char *GetClassName() { return "AITown"; }
+
+ /**
+ * Actions that one can perform on a town.
+ */
+ enum TownAction {
+ /* Values are important, as they represent the internal state of the game. */
+
+ /**
+ * The cargo ratings temporary gains 25% of rating (in
+ * absolute percentage, so 10% becomes 35%, with a max of 99%)
+ * for all stations within 10 tiles.
+ */
+ TOWN_ACTION_ADVERTISE_SMALL = 0,
+
+ /**
+ * The cargo ratings temporary gains 44% of rating (in
+ * absolute percentage, so 10% becomes 54%, with a max of 99%)
+ * for all stations within 15 tiles.
+ */
+ TOWN_ACTION_ADVERTISE_MEDIUM = 1,
+
+ /**
+ * The cargo ratings temporary gains 63% of rating (in
+ * absolute percentage, so 10% becomes 73%, with a max of 99%)
+ * for all stations within 20 tiles.
+ */
+ TOWN_ACTION_ADVERTISE_LARGE = 2,
+
+ /**
+ * Rebuild the roads of this town for 6 months.
+ */
+ TOWN_ACTION_ROAD_REBUILD = 3,
+
+ /**
+ * Build a statue in this town.
+ */
+ TOWN_ACTION_BUILD_STATUE = 4,
+
+ /**
+ * Fund the creation of extra buildings for 3 months.
+ */
+ TOWN_ACTION_FUND_BUILDINGS = 5,
+
+ /**
+ * Buy exclusive rights for this town for 12 months.
+ */
+ TOWN_ACTION_BUY_RIGHTS = 6,
+
+ /**
+ * Bribe the town in order to get a higher rating.
+ */
+ TOWN_ACTION_BRIBE = 7,
+ };
+
+ /**
+ * Different ratings one could have in a town.
+ */
+ enum TownRating {
+ TOWN_RATING_NONE, ///< The company got no rating in the town.
+ TOWN_RATING_APPALLING, ///< The company got an appalling rating in the town .
+ TOWN_RATING_VERY_POOR, ///< The company got an very poor rating in the town.
+ TOWN_RATING_POOR, ///< The company got an poor rating in the town.
+ TOWN_RATING_MEDIOCRE, ///< The company got an mediocre rating in the town.
+ TOWN_RATING_GOOD, ///< The company got an good rating in the town.
+ TOWN_RATING_VERY_GOOD, ///< The company got an very good rating in the town.
+ TOWN_RATING_EXCELLENT, ///< The company got an excellent rating in the town.
+ TOWN_RATING_OUTSTANDING, ///< The company got an outstanding rating in the town.
+ INVALID_TOWN_RATING = -1, ///< The town rating for invalid towns/companies.
+ };
+
+ /**
+ * Gets the maximum town index; there are no valid towns with a higher index.
+ * @return The maximum town index.
+ * @post Return value is always non-negative.
+ */
+ static TownID GetMaxTownID();
+
+ /**
+ * Gets the number of towns. This is different than GetMaxTownID()
+ * because of the way OpenTTD works internally.
+ * @return The number of towns.
+ * @post Return value is always non-negative.
+ */
+ static int32 GetTownCount();
+
+ /**
+ * Checks whether the given town index is valid.
+ * @param town_id The index to check.
+ * @return True if and only if the town is valid.
+ */
+ static bool IsValidTown(TownID town_id);
+
+ /**
+ * Get the name of the town.
+ * @param town_id The town to get the name of.
+ * @pre IsValidTown(town_id).
+ * @return The name of the town.
+ */
+ static const char *GetName(TownID town_id);
+
+ /**
+ * Gets the number of inhabitants in the town.
+ * @param town_id The town to get the population of.
+ * @pre IsValidTown(town_id).
+ * @return The number of inhabitants.
+ * @post Return value is always non-negative.
+ */
+ static int32 GetPopulation(TownID town_id);
+
+ /**
+ * Gets the number of houses in the town.
+ * @param town_id The town to get the number of houses of.
+ * @pre IsValidTown(town_id).
+ * @return The number of houses.
+ * @post Return value is always non-negative.
+ */
+ static int32 GetHouseCount(TownID town_id);
+
+ /**
+ * Gets the location of the town.
+ * @param town_id The town to get the location of.
+ * @pre IsValidTown(town_id).
+ * @return The location of the town.
+ */
+ static TileIndex GetLocation(TownID town_id);
+
+ /**
+ * Get the total last month's production of the given cargo at a town.
+ * @param town_id The index of the town.
+ * @param cargo_id The index of the cargo.
+ * @pre IsValidTown(town_id).
+ * @pre AICargo::IsValidCargo(cargo_id).
+ * @pre AICargo::GetTownEffect(cargo_id) == TE_PASSENGERS || AICargo::GetTownEffect(cargo_id) == TE_MAIL.
+ * @return The last month's production of the given cargo for this town.
+ * @post Return value is always non-negative.
+ */
+ static int32 GetLastMonthProduction(TownID town_id, CargoID cargo_id);
+
+ /**
+ * Get the total amount of cargo transported from a town last month.
+ * @param town_id The index of the industry.
+ * @param cargo_id The index of the cargo.
+ * @pre IsValidTown(town_id).
+ * @pre AICargo::IsValidCargo(cargo_id).
+ * @pre AICargo::GetTownEffect(cargo_id) == TE_PASSENGERS || AICargo::GetTownEffect(cargo_id) == TE_MAIL.
+ * @return The amount of given cargo transported from this town last month.
+ * @post Return value is always non-negative.
+ */
+ static int32 GetLastMonthTransported(TownID town_id, CargoID cargo_id);
+
+ /**
+ * Get the maximum production of the given cargo at a town.
+ * @param town_id The index of the town.
+ * @param cargo_id The index of the cargo.
+ * @pre IsValidTown(town_id).
+ * @pre AICargo::IsValidCargo(cargo_id).
+ * @pre AICargo::GetTownEffect(cargo_id) == TE_PASSENGERS || AICargo::GetTownEffect(cargo_id) == TE_MAIL.
+ * @return The maximum production of the given cargo for this town.
+ * @post Return value is always non-negative.
+ */
+ static int32 GetMaxProduction(TownID town_id, CargoID cargo_id);
+
+ /**
+ * Get the manhattan distance from the tile to the AITown::GetLocation()
+ * of the town.
+ * @param town_id The town to get the distance to.
+ * @param tile The tile to get the distance to.
+ * @pre IsValidTown(town_id).
+ * @return The distance between town and tile.
+ */
+ static int32 GetDistanceManhattanToTile(TownID town_id, TileIndex tile);
+
+ /**
+ * Get the square distance from the tile to the AITown::GetLocation()
+ * of the town.
+ * @param town_id The town to get the distance to.
+ * @param tile The tile to get the distance to.
+ * @pre IsValidTown(town_id).
+ * @return The distance between town and tile.
+ */
+ static int32 GetDistanceSquareToTile(TownID town_id, TileIndex tile);
+
+ /**
+ * Find out if this tile is within the rating influence of a town.
+ * Stations on this tile influence the rating of the town.
+ * @param town_id The town to check.
+ * @param tile The tile to check.
+ * @pre IsValidTown(town_id).
+ * @return True if the tile is within the rating influence of the town.
+ */
+ static bool IsWithinTownInfluence(TownID town_id, TileIndex tile);
+
+ /**
+ * Find out if this town has a statue for the current company.
+ * @param town_id The town to check.
+ * @pre IsValidTown(town_id).
+ * @return True if the town has a statue.
+ */
+ static bool HasStatue(TownID town_id);
+
+ /**
+ * Find out how long the town is undergoing road reconstructions.
+ * @param town_id The town to check.
+ * @pre IsValidTown(town_id).
+ * @return The number of months the road reworks are still going to take.
+ * The value 0 means that there are currently no road reworks.
+ */
+ static int GetRoadReworkDuration(TownID town_id);
+
+ /**
+ * Find out which company currently has the exclusive rights of this town.
+ * @param town_id The town to check.
+ * @pre IsValidTown(town_id).
+ * @return The company that has the exclusive rights. The value
+ * AICompany::INVALID_COMPANY means that there are currently no
+ * exclusive rights given out to anyone.
+ */
+ static AICompany::CompanyID GetExclusiveRightsCompany(TownID town_id);
+
+ /**
+ * Find out how long the town is under influence of the exclusive rights.
+ * @param town_id The town to check.
+ * @pre IsValidTown(town_id).
+ * @return The number of months the exclusive rights hold.
+ * The value 0 means that there are currently no exclusive rights
+ * given out to anyone.
+ */
+ static int32 GetExclusiveRightsDuration(TownID town_id);
+
+ /**
+ * Find out if an action can currently be performed on the town.
+ * @param town_id The town to perform the action on.
+ * @param town_action The action to perform on the town.
+ * @pre IsValidTown(town_id).
+ * @return True if and only if the action can performed.
+ */
+ static bool IsActionAvailable(TownID town_id, TownAction town_action);
+
+ /**
+ * Perform a town action on this town.
+ * @param town_id The town to perform the action on.
+ * @param town_action The action to perform on the town.
+ * @pre IsValidTown(town_id).
+ * @pre IsActionAvailable(town_id, town_action).
+ * @return True if the action succeeded.
+ */
+ static bool PerformTownAction(TownID town_id, TownAction town_action);
+
+ /**
+ * Get the rating of a company within a town.
+ * @param town_id The town to get the rating for.
+ * @param company_id The company to get the rating for.
+ * @pre IsValidTown(town_id).
+ * @pre AICompany.ResolveCompanyID(company) != AICompany::INVALID_COMPANY.
+ * @return The rating as shown to humans.
+ */
+ static TownRating GetRating(TownID town_id, AICompany::CompanyID company_id);
+
+ /**
+ * Get the maximum level of noise that still can be added by airports
+ * before the town start to refuse building a new airport.
+ * @param town_id The town to get the allowed noise from.
+ * @return The noise that still can be added.
+ */
+ static int GetAllowedNoise(TownID town_id);
+};
+
+#endif /* AI_TOWN_HPP */
diff --git a/src/ai/api/ai_town.hpp.sq b/src/ai/api/ai_town.hpp.sq
new file mode 100644
index 000000000..664503923
--- /dev/null
+++ b/src/ai/api/ai_town.hpp.sq
@@ -0,0 +1,69 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_town.hpp"
+
+namespace SQConvert {
+ /* Allow enums to be used as Squirrel parameters */
+ template <> AITown::TownAction GetParam(ForceType<AITown::TownAction>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AITown::TownAction)tmp; }
+ template <> int Return<AITown::TownAction>(HSQUIRRELVM vm, AITown::TownAction res) { sq_pushinteger(vm, (int32)res); return 1; }
+ template <> AITown::TownRating GetParam(ForceType<AITown::TownRating>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AITown::TownRating)tmp; }
+ template <> int Return<AITown::TownRating>(HSQUIRRELVM vm, AITown::TownRating res) { sq_pushinteger(vm, (int32)res); return 1; }
+
+ /* Allow AITown to be used as Squirrel parameter */
+ template <> AITown *GetParam(ForceType<AITown *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITown *)instance; }
+ template <> AITown &GetParam(ForceType<AITown &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITown *)instance; }
+ template <> const AITown *GetParam(ForceType<const AITown *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITown *)instance; }
+ template <> const AITown &GetParam(ForceType<const AITown &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITown *)instance; }
+ template <> int Return<AITown *>(HSQUIRRELVM vm, AITown *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AITown", res, NULL, DefSQDestructorCallback<AITown>); return 1; }
+}; // namespace SQConvert
+
+void SQAITown_Register(Squirrel *engine) {
+ DefSQClass <AITown> SQAITown("AITown");
+ SQAITown.PreRegister(engine);
+ SQAITown.AddConstructor<void (AITown::*)(), 1>(engine, "x");
+
+ SQAITown.DefSQConst(engine, AITown::TOWN_ACTION_ADVERTISE_SMALL, "TOWN_ACTION_ADVERTISE_SMALL");
+ SQAITown.DefSQConst(engine, AITown::TOWN_ACTION_ADVERTISE_MEDIUM, "TOWN_ACTION_ADVERTISE_MEDIUM");
+ SQAITown.DefSQConst(engine, AITown::TOWN_ACTION_ADVERTISE_LARGE, "TOWN_ACTION_ADVERTISE_LARGE");
+ SQAITown.DefSQConst(engine, AITown::TOWN_ACTION_ROAD_REBUILD, "TOWN_ACTION_ROAD_REBUILD");
+ SQAITown.DefSQConst(engine, AITown::TOWN_ACTION_BUILD_STATUE, "TOWN_ACTION_BUILD_STATUE");
+ SQAITown.DefSQConst(engine, AITown::TOWN_ACTION_FUND_BUILDINGS, "TOWN_ACTION_FUND_BUILDINGS");
+ SQAITown.DefSQConst(engine, AITown::TOWN_ACTION_BUY_RIGHTS, "TOWN_ACTION_BUY_RIGHTS");
+ SQAITown.DefSQConst(engine, AITown::TOWN_ACTION_BRIBE, "TOWN_ACTION_BRIBE");
+ SQAITown.DefSQConst(engine, AITown::TOWN_RATING_NONE, "TOWN_RATING_NONE");
+ SQAITown.DefSQConst(engine, AITown::TOWN_RATING_APPALLING, "TOWN_RATING_APPALLING");
+ SQAITown.DefSQConst(engine, AITown::TOWN_RATING_VERY_POOR, "TOWN_RATING_VERY_POOR");
+ SQAITown.DefSQConst(engine, AITown::TOWN_RATING_POOR, "TOWN_RATING_POOR");
+ SQAITown.DefSQConst(engine, AITown::TOWN_RATING_MEDIOCRE, "TOWN_RATING_MEDIOCRE");
+ SQAITown.DefSQConst(engine, AITown::TOWN_RATING_GOOD, "TOWN_RATING_GOOD");
+ SQAITown.DefSQConst(engine, AITown::TOWN_RATING_VERY_GOOD, "TOWN_RATING_VERY_GOOD");
+ SQAITown.DefSQConst(engine, AITown::TOWN_RATING_EXCELLENT, "TOWN_RATING_EXCELLENT");
+ SQAITown.DefSQConst(engine, AITown::TOWN_RATING_OUTSTANDING, "TOWN_RATING_OUTSTANDING");
+ SQAITown.DefSQConst(engine, AITown::INVALID_TOWN_RATING, "INVALID_TOWN_RATING");
+
+ SQAITown.DefSQStaticMethod(engine, &AITown::GetClassName, "GetClassName", 1, "x");
+ SQAITown.DefSQStaticMethod(engine, &AITown::GetMaxTownID, "GetMaxTownID", 1, "x");
+ SQAITown.DefSQStaticMethod(engine, &AITown::GetTownCount, "GetTownCount", 1, "x");
+ SQAITown.DefSQStaticMethod(engine, &AITown::IsValidTown, "IsValidTown", 2, "xi");
+ SQAITown.DefSQStaticMethod(engine, &AITown::GetName, "GetName", 2, "xi");
+ SQAITown.DefSQStaticMethod(engine, &AITown::GetPopulation, "GetPopulation", 2, "xi");
+ SQAITown.DefSQStaticMethod(engine, &AITown::GetHouseCount, "GetHouseCount", 2, "xi");
+ SQAITown.DefSQStaticMethod(engine, &AITown::GetLocation, "GetLocation", 2, "xi");
+ SQAITown.DefSQStaticMethod(engine, &AITown::GetLastMonthProduction, "GetLastMonthProduction", 3, "xii");
+ SQAITown.DefSQStaticMethod(engine, &AITown::GetLastMonthTransported, "GetLastMonthTransported", 3, "xii");
+ SQAITown.DefSQStaticMethod(engine, &AITown::GetMaxProduction, "GetMaxProduction", 3, "xii");
+ SQAITown.DefSQStaticMethod(engine, &AITown::GetDistanceManhattanToTile, "GetDistanceManhattanToTile", 3, "xii");
+ SQAITown.DefSQStaticMethod(engine, &AITown::GetDistanceSquareToTile, "GetDistanceSquareToTile", 3, "xii");
+ SQAITown.DefSQStaticMethod(engine, &AITown::IsWithinTownInfluence, "IsWithinTownInfluence", 3, "xii");
+ SQAITown.DefSQStaticMethod(engine, &AITown::HasStatue, "HasStatue", 2, "xi");
+ SQAITown.DefSQStaticMethod(engine, &AITown::GetRoadReworkDuration, "GetRoadReworkDuration", 2, "xi");
+ SQAITown.DefSQStaticMethod(engine, &AITown::GetExclusiveRightsCompany, "GetExclusiveRightsCompany", 2, "xi");
+ SQAITown.DefSQStaticMethod(engine, &AITown::GetExclusiveRightsDuration, "GetExclusiveRightsDuration", 2, "xi");
+ SQAITown.DefSQStaticMethod(engine, &AITown::IsActionAvailable, "IsActionAvailable", 3, "xii");
+ SQAITown.DefSQStaticMethod(engine, &AITown::PerformTownAction, "PerformTownAction", 3, "xii");
+ SQAITown.DefSQStaticMethod(engine, &AITown::GetRating, "GetRating", 3, "xii");
+ SQAITown.DefSQStaticMethod(engine, &AITown::GetAllowedNoise, "GetAllowedNoise", 2, "xi");
+
+ SQAITown.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_townlist.cpp b/src/ai/api/ai_townlist.cpp
new file mode 100644
index 000000000..34612d857
--- /dev/null
+++ b/src/ai/api/ai_townlist.cpp
@@ -0,0 +1,15 @@
+/* $Id$ */
+
+/** @file ai_townlist.cpp Implementation of AITownList and friends. */
+
+#include "ai_townlist.hpp"
+#include "../../openttd.h"
+#include "../../town.h"
+
+AITownList::AITownList()
+{
+ Town *t;
+ FOR_ALL_TOWNS(t) {
+ this->AddItem(t->index);
+ }
+}
diff --git a/src/ai/api/ai_townlist.hpp b/src/ai/api/ai_townlist.hpp
new file mode 100644
index 000000000..0e2eb1530
--- /dev/null
+++ b/src/ai/api/ai_townlist.hpp
@@ -0,0 +1,20 @@
+/* $Id$ */
+
+/** @file ai_townlist.hpp List all the towns. */
+
+#ifndef AI_TOWNLIST_HPP
+#define AI_TOWNLIST_HPP
+
+#include "ai_abstractlist.hpp"
+
+/**
+ * Creates a list of towns that are currently on the map.
+ * @ingroup AIList
+ */
+class AITownList : public AIAbstractList {
+public:
+ static const char *GetClassName() { return "AITownList"; }
+ AITownList();
+};
+
+#endif /* AI_TOWNLIST_HPP */
diff --git a/src/ai/api/ai_townlist.hpp.sq b/src/ai/api/ai_townlist.hpp.sq
new file mode 100644
index 000000000..31be65144
--- /dev/null
+++ b/src/ai/api/ai_townlist.hpp.sq
@@ -0,0 +1,23 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_townlist.hpp"
+
+namespace SQConvert {
+ /* Allow AITownList to be used as Squirrel parameter */
+ template <> AITownList *GetParam(ForceType<AITownList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITownList *)instance; }
+ template <> AITownList &GetParam(ForceType<AITownList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITownList *)instance; }
+ template <> const AITownList *GetParam(ForceType<const AITownList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITownList *)instance; }
+ template <> const AITownList &GetParam(ForceType<const AITownList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITownList *)instance; }
+ template <> int Return<AITownList *>(HSQUIRRELVM vm, AITownList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AITownList", res, NULL, DefSQDestructorCallback<AITownList>); return 1; }
+}; // namespace SQConvert
+
+void SQAITownList_Register(Squirrel *engine) {
+ DefSQClass <AITownList> SQAITownList("AITownList");
+ SQAITownList.PreRegister(engine, "AIAbstractList");
+ SQAITownList.AddConstructor<void (AITownList::*)(), 1>(engine, "x");
+
+ SQAITownList.DefSQStaticMethod(engine, &AITownList::GetClassName, "GetClassName", 1, "x");
+
+ SQAITownList.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_tunnel.cpp b/src/ai/api/ai_tunnel.cpp
new file mode 100644
index 000000000..1991ef89b
--- /dev/null
+++ b/src/ai/api/ai_tunnel.cpp
@@ -0,0 +1,119 @@
+/* $Id$ */
+
+/** @file ai_tunnel.cpp Implementation of AITunnel. */
+
+#include "ai_tunnel.hpp"
+#include "ai_map.hpp"
+#include "ai_rail.hpp"
+#include "../ai_instance.hpp"
+#include "../../openttd.h"
+#include "../../landscape.h"
+#include "../../tunnel_map.h"
+#include "../../road_type.h"
+#include "../../command_func.h"
+#include "../../tunnelbridge.h"
+#include "../../road_func.h"
+
+/* static */ bool AITunnel::IsTunnelTile(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return false;
+ return ::IsTunnelTile(tile);
+}
+
+/* static */ TileIndex AITunnel::GetOtherTunnelEnd(TileIndex tile)
+{
+ if (!::IsValidTile(tile)) return INVALID_TILE;
+
+ /* If it's a tunnel alread, take the easy way out! */
+ if (IsTunnelTile(tile)) return ::GetOtherTunnelEnd(tile);
+
+ ::DoCommand(tile, 0, 0, DC_AUTO, CMD_BUILD_TUNNEL);
+ return _build_tunnel_endtile == 0 ? INVALID_TILE : _build_tunnel_endtile;
+}
+
+static void _DoCommandReturnBuildTunnel2(class AIInstance *instance)
+{
+ if (!AITunnel::_BuildTunnelRoad2()) {
+ 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 _DoCommandReturnBuildTunnel1(class AIInstance *instance)
+{
+ if (!AITunnel::_BuildTunnelRoad1()) {
+ 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 AITunnel::BuildTunnel(AIVehicle::VehicleType vehicle_type, TileIndex start)
+{
+ EnforcePrecondition(false, ::IsValidTile(start));
+ EnforcePrecondition(false, vehicle_type == AIVehicle::VEHICLE_RAIL || vehicle_type == AIVehicle::VEHICLE_ROAD);
+ EnforcePrecondition(false, vehicle_type != AIVehicle::VEHICLE_RAIL || AIRail::IsRailTypeAvailable(AIRail::GetCurrentRailType()));
+
+ uint type = 0;
+ if (vehicle_type == AIVehicle::VEHICLE_ROAD) {
+ type |= (TRANSPORT_ROAD << 9);
+ type |= RoadTypeToRoadTypes((::RoadType)AIObject::GetRoadType());
+ } else {
+ type |= (TRANSPORT_RAIL << 9);
+ type |= AIRail::GetCurrentRailType();
+ }
+
+ /* For rail we do nothing special */
+ if (vehicle_type == AIVehicle::VEHICLE_RAIL) {
+ return AIObject::DoCommand(start, type, 0, CMD_BUILD_TUNNEL);
+ }
+
+ AIObject::SetCallbackVariable(0, start);
+ if (!AIObject::DoCommand(start, type, 0, CMD_BUILD_TUNNEL, NULL, &_DoCommandReturnBuildTunnel1)) return false;
+
+ /* In case of test-mode, test if we can build both road pieces */
+ return _BuildTunnelRoad1();
+}
+
+/* static */ bool AITunnel::_BuildTunnelRoad1()
+{
+ /* Build the piece of road on the 'start' side of the tunnel */
+ TileIndex end = AIObject::GetCallbackVariable(0);
+ TileIndex start = AITunnel::GetOtherTunnelEnd(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, &_DoCommandReturnBuildTunnel2)) return false;
+
+ /* In case of test-mode, test the other road piece too */
+ return _BuildTunnelRoad2();
+}
+
+/* static */ bool AITunnel::_BuildTunnelRoad2()
+{
+ /* Build the piece of road on the 'end' side of the tunnel */
+ TileIndex end = AIObject::GetCallbackVariable(0);
+ TileIndex start = AITunnel::GetOtherTunnelEnd(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 AITunnel::RemoveTunnel(TileIndex tile)
+{
+ EnforcePrecondition(false, IsTunnelTile(tile));
+
+ return AIObject::DoCommand(tile, 0, 0, CMD_LANDSCAPE_CLEAR);
+}
diff --git a/src/ai/api/ai_tunnel.hpp b/src/ai/api/ai_tunnel.hpp
new file mode 100644
index 000000000..625237905
--- /dev/null
+++ b/src/ai/api/ai_tunnel.hpp
@@ -0,0 +1,103 @@
+/* $Id$ */
+
+/** @file ai_tunnel.hpp Everything to query and build tunnels. */
+
+#ifndef AI_TUNNEL_HPP
+#define AI_TUNNEL_HPP
+
+#include "ai_object.hpp"
+#include "ai_vehicle.hpp"
+
+/**
+ * Class that handles all tunnel related functions.
+ */
+class AITunnel : public AIObject {
+public:
+ static const char *GetClassName() { return "AITunnel"; }
+
+ /**
+ * All tunnel related errors.
+ */
+ enum ErrorMessages {
+
+ /** Base for bridge related errors */
+ ERR_TUNNEL_BASE = AIError::ERR_CAT_TUNNEL << AIError::ERR_CAT_BIT_SIZE,
+
+ /** Can't build tunnels on water */
+ ERR_TUNNEL_CANNOT_BUILD_ON_WATER, // [STR_3807_CAN_T_BUILD_ON_WATER]
+
+ /** The start tile must slope either North, South, West or East */
+ ERR_TUNNEL_START_SITE_UNSUITABLE, // [STR_500B_SITE_UNSUITABLE_FOR_TUNNEL]
+
+ /** An other tunnel is in the way */
+ ERR_TUNNEL_ANOTHER_TUNNEL_IN_THE_WAY, // [STR_5003_ANOTHER_TUNNEL_IN_THE_WAY]
+
+ /** Unable to excavate land at the end to create the tunnel's exit */
+ ERR_TUNNEL_END_SITE_UNSUITABLE, // [STR_5005_UNABLE_TO_EXCAVATE_LAND]
+ };
+
+ /**
+ * Check whether the tile is an entrance to a tunnel.
+ * @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 tunnel.
+ */
+ static bool IsTunnelTile(TileIndex tile);
+
+ /**
+ * Get the tile that exits on the other end of a (would be) tunnel starting
+ * at tile.
+ * @param tile The tile that is an entrance to a tunnel or the tile where you may want to build a tunnel.
+ * @pre AIMap::IsValidTile(tile).
+ * @return The TileIndex that is the other end of the (would be) tunnel, or
+ * INVALID_TILE if no other end was found (can't build tunnel).
+ */
+ static TileIndex GetOtherTunnelEnd(TileIndex tile);
+
+#ifndef DOXYGEN_SKIP
+ /**
+ * Internal function to help BuildTunnel in case of road.
+ */
+ static bool _BuildTunnelRoad1();
+
+ /**
+ * Internal function to help BuildTunnel in case of road.
+ */
+ static bool _BuildTunnelRoad2();
+#endif
+
+ /**
+ * Builds a tunnel starting at start. The direction of the tunnel depends
+ * on the slope of the start tile. Tunnels can be created for either
+ * rails or roads; use the appropriate AIVehicle::VehicleType.
+ * As an extra for road, this functions builds two half-pieces of road on
+ * each end of the tunnel, making it easier for you to connect it to your
+ * network.
+ * @param start Where to start the tunnel.
+ * @param vehicle_type The vehicle-type of tunnel to build.
+ * @pre AIMap::IsValidTile(start).
+ * @pre vehicle_type == AIVehicle::VEHICLE_ROAD || (vehicle_type == AIVehicle::VEHICLE_RAIL &&
+ * AIRail::IsRailTypeAvailable(AIRail::GetCurrentRailType())).
+ * @exception AIError::ERR_AREA_NOT_CLEAR
+ * @exception AITunnel::ERR_TUNNEL_CANNOT_BUILD_ON_WATER
+ * @exception AITunnel::ERR_TUNNEL_START_SITE_UNSUITABLE
+ * @exception AITunnel::ERR_TUNNEL_ANOTHER_TUNNEL_IN_THE_WAY
+ * @exception AITunnel::ERR_TUNNEL_END_SITE_UNSUITABLE
+ * @return Whether the tunnel has been/can be build or not.
+ * @note The slope of a tile can be determined by AITile::GetSlope(TileIndex).
+ * @note No matter if the road pieces were build or not, if building the
+ * tunnel succeeded, this function returns true.
+ */
+ static bool BuildTunnel(AIVehicle::VehicleType vehicle_type, TileIndex start);
+
+ /**
+ * Remove the tunnel whose entrance is located at tile.
+ * @param tile The tile that is an entrance to a tunnel.
+ * @pre AIMap::IsValidTile(tile) && IsTunnelTile(tile).
+ * @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
+ * @return Whether the tunnel has been/can be removed or not.
+ */
+ static bool RemoveTunnel(TileIndex tile);
+};
+
+#endif /* AI_TUNNEL_HPP */
diff --git a/src/ai/api/ai_tunnel.hpp.sq b/src/ai/api/ai_tunnel.hpp.sq
new file mode 100644
index 000000000..fd2f6959c
--- /dev/null
+++ b/src/ai/api/ai_tunnel.hpp.sq
@@ -0,0 +1,47 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_tunnel.hpp"
+
+namespace SQConvert {
+ /* Allow enums to be used as Squirrel parameters */
+ template <> AITunnel::ErrorMessages GetParam(ForceType<AITunnel::ErrorMessages>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AITunnel::ErrorMessages)tmp; }
+ template <> int Return<AITunnel::ErrorMessages>(HSQUIRRELVM vm, AITunnel::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; }
+
+ /* Allow AITunnel to be used as Squirrel parameter */
+ template <> AITunnel *GetParam(ForceType<AITunnel *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITunnel *)instance; }
+ template <> AITunnel &GetParam(ForceType<AITunnel &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITunnel *)instance; }
+ template <> const AITunnel *GetParam(ForceType<const AITunnel *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AITunnel *)instance; }
+ template <> const AITunnel &GetParam(ForceType<const AITunnel &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AITunnel *)instance; }
+ template <> int Return<AITunnel *>(HSQUIRRELVM vm, AITunnel *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AITunnel", res, NULL, DefSQDestructorCallback<AITunnel>); return 1; }
+}; // namespace SQConvert
+
+void SQAITunnel_Register(Squirrel *engine) {
+ DefSQClass <AITunnel> SQAITunnel("AITunnel");
+ SQAITunnel.PreRegister(engine);
+ SQAITunnel.AddConstructor<void (AITunnel::*)(), 1>(engine, "x");
+
+ SQAITunnel.DefSQConst(engine, AITunnel::ERR_TUNNEL_BASE, "ERR_TUNNEL_BASE");
+ SQAITunnel.DefSQConst(engine, AITunnel::ERR_TUNNEL_CANNOT_BUILD_ON_WATER, "ERR_TUNNEL_CANNOT_BUILD_ON_WATER");
+ SQAITunnel.DefSQConst(engine, AITunnel::ERR_TUNNEL_START_SITE_UNSUITABLE, "ERR_TUNNEL_START_SITE_UNSUITABLE");
+ SQAITunnel.DefSQConst(engine, AITunnel::ERR_TUNNEL_ANOTHER_TUNNEL_IN_THE_WAY, "ERR_TUNNEL_ANOTHER_TUNNEL_IN_THE_WAY");
+ SQAITunnel.DefSQConst(engine, AITunnel::ERR_TUNNEL_END_SITE_UNSUITABLE, "ERR_TUNNEL_END_SITE_UNSUITABLE");
+
+ AIError::RegisterErrorMap(STR_3807_CAN_T_BUILD_ON_WATER, AITunnel::ERR_TUNNEL_CANNOT_BUILD_ON_WATER);
+ AIError::RegisterErrorMap(STR_500B_SITE_UNSUITABLE_FOR_TUNNEL, AITunnel::ERR_TUNNEL_START_SITE_UNSUITABLE);
+ AIError::RegisterErrorMap(STR_5003_ANOTHER_TUNNEL_IN_THE_WAY, AITunnel::ERR_TUNNEL_ANOTHER_TUNNEL_IN_THE_WAY);
+ AIError::RegisterErrorMap(STR_5005_UNABLE_TO_EXCAVATE_LAND, AITunnel::ERR_TUNNEL_END_SITE_UNSUITABLE);
+
+ AIError::RegisterErrorMapString(AITunnel::ERR_TUNNEL_CANNOT_BUILD_ON_WATER, "ERR_TUNNEL_CANNOT_BUILD_ON_WATER");
+ AIError::RegisterErrorMapString(AITunnel::ERR_TUNNEL_START_SITE_UNSUITABLE, "ERR_TUNNEL_START_SITE_UNSUITABLE");
+ AIError::RegisterErrorMapString(AITunnel::ERR_TUNNEL_ANOTHER_TUNNEL_IN_THE_WAY, "ERR_TUNNEL_ANOTHER_TUNNEL_IN_THE_WAY");
+ AIError::RegisterErrorMapString(AITunnel::ERR_TUNNEL_END_SITE_UNSUITABLE, "ERR_TUNNEL_END_SITE_UNSUITABLE");
+
+ SQAITunnel.DefSQStaticMethod(engine, &AITunnel::GetClassName, "GetClassName", 1, "x");
+ SQAITunnel.DefSQStaticMethod(engine, &AITunnel::IsTunnelTile, "IsTunnelTile", 2, "xi");
+ SQAITunnel.DefSQStaticMethod(engine, &AITunnel::GetOtherTunnelEnd, "GetOtherTunnelEnd", 2, "xi");
+ SQAITunnel.DefSQStaticMethod(engine, &AITunnel::BuildTunnel, "BuildTunnel", 3, "xii");
+ SQAITunnel.DefSQStaticMethod(engine, &AITunnel::RemoveTunnel, "RemoveTunnel", 2, "xi");
+
+ SQAITunnel.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_types.hpp b/src/ai/api/ai_types.hpp
new file mode 100644
index 000000000..086cf75e0
--- /dev/null
+++ b/src/ai/api/ai_types.hpp
@@ -0,0 +1,40 @@
+/* $Id$ */
+
+/** @file ai_types.hpp Defines all the types of the game, like VehicleID, .... */
+
+#ifndef AI_TYPES_HPP
+#define AI_TYPES_HPP
+
+#include "../../core/overflowsafe_type.hpp"
+#include "../../company_type.h"
+
+/* Define all types here, so we don't have to include the whole _type.h maze */
+typedef uint BridgeType; //!< Internal name, not of any use for you.
+typedef byte CargoID; //!< The ID of a cargo.
+class CommandCost; //!< The cost of a command.
+typedef uint16 EngineID; //!< The ID of an engine.
+typedef uint16 GroupID; //!< The ID of a group.
+typedef uint16 IndustryID; //!< The ID of an industry.
+typedef uint8 IndustryType; //!< The ID of an industry-type.
+typedef OverflowSafeInt64 Money; //!< Money, stored in a 32bit/64bit safe way. For AIs money is always in pounds.
+typedef uint16 SignID; //!< The ID of a sign.
+typedef uint16 StationID; //!< The ID of a station.
+typedef uint16 StringID; //!< The ID of a string.
+typedef uint32 TileIndex; //!< The ID of a tile (just named differently).
+typedef uint16 TownID; //!< The ID of a town.
+typedef uint16 VehicleID; //!< The ID of a vehicle.
+
+/* Types we defined ourself, as the OpenTTD core doesn't have them (yet) */
+typedef uint AIErrorType; //!< The types of errors inside the NoAI framework.
+typedef BridgeType BridgeID; //!< The ID of a bridge.
+typedef uint16 SubsidyID; //!< The ID of a subsidy.
+
+#ifndef _SQUIRREL_H_
+/* Life becomes easier when we can tell about a function it needs the VM, but
+ * without really including 'squirrel.h'. */
+typedef struct SQVM *HSQUIRRELVM; //!< Pointer to Squirrel Virtual Machine.
+typedef int SQInteger; //!< Squirrel Integer.
+typedef struct SQObject HSQOBJECT; //!< Squirrel Object (fake declare)
+#endif
+
+#endif /* AI_TYPES_HPP */
diff --git a/src/ai/api/ai_vehicle.cpp b/src/ai/api/ai_vehicle.cpp
new file mode 100644
index 000000000..9532e3caf
--- /dev/null
+++ b/src/ai/api/ai_vehicle.cpp
@@ -0,0 +1,389 @@
+/* $Id$ */
+
+/** @file ai_vehicle.cpp Implementation of AIVehicle. */
+
+#include "ai_vehicle.hpp"
+#include "ai_engine.hpp"
+#include "ai_cargo.hpp"
+#include "ai_order.hpp"
+#include "ai_gamesettings.hpp"
+#include "../ai_instance.hpp"
+#include "../../openttd.h"
+#include "../../company_func.h"
+#include "../../aircraft.h"
+#include "../../string_func.h"
+#include "../../strings_func.h"
+#include "../../core/alloc_func.hpp"
+#include "../../command_func.h"
+#include "../../roadveh.h"
+#include "../../train.h"
+#include "../../vehicle_func.h"
+#include "table/strings.h"
+
+/* static */ bool AIVehicle::IsValidVehicle(VehicleID vehicle_id)
+{
+ if (!::IsValidVehicleID(vehicle_id)) return false;
+ const Vehicle *v = ::GetVehicle(vehicle_id);
+ return v->owner == _current_company && (v->IsPrimaryVehicle() || (v->type == VEH_TRAIN && ::IsFreeWagon(v)));
+}
+
+/* static */ int32 AIVehicle::GetNumWagons(VehicleID vehicle_id)
+{
+ if (!IsValidVehicle(vehicle_id)) return -1;
+
+ int num = 1;
+ if (::GetVehicle(vehicle_id)->type == VEH_TRAIN) {
+ const Vehicle *v = ::GetVehicle(vehicle_id);
+ while ((v = GetNextUnit(v)) != NULL) num++;
+ }
+
+ return num;
+}
+
+/* static */ int AIVehicle::GetLength(VehicleID vehicle_id)
+{
+ if (!IsValidVehicle(vehicle_id)) return -1;
+
+ const Vehicle *v = ::GetVehicle(vehicle_id);
+ switch (v->type) {
+ case VEH_ROAD: return v->u.road.cached_veh_length;
+ case VEH_TRAIN: return v->u.rail.cached_total_length;
+ default: return -1;
+ }
+}
+
+/* static */ VehicleID AIVehicle::BuildVehicle(TileIndex depot, EngineID engine_id)
+{
+ EnforcePrecondition(INVALID_VEHICLE, AIEngine::IsValidEngine(engine_id));
+
+ ::VehicleType type = ::GetEngine(engine_id)->type;
+
+ EnforcePreconditionCustomError(INVALID_VEHICLE, !AIGameSettings::IsDisabledVehicleType((AIVehicle::VehicleType)type), AIVehicle::ERR_VEHICLE_BUILD_DISABLED);
+
+ if (!AIObject::DoCommand(depot, engine_id, 0, ::GetCmdBuildVeh(type), NULL, &AIInstance::DoCommandReturnVehicleID)) return INVALID_VEHICLE;
+
+ /* In case of test-mode, we return VehicleID 0 */
+ return 0;
+}
+
+/* static */ VehicleID AIVehicle::CloneVehicle(TileIndex depot, VehicleID vehicle_id, bool share_orders)
+{
+ EnforcePrecondition(false, IsValidVehicle(vehicle_id));
+
+ if (!AIObject::DoCommand(depot, vehicle_id, share_orders, CMD_CLONE_VEHICLE, NULL, &AIInstance::DoCommandReturnVehicleID)) return INVALID_VEHICLE;
+
+ /* In case of test-mode, we return VehicleID 0 */
+ return 0;
+}
+
+/* static */ bool AIVehicle::MoveWagon(VehicleID source_vehicle_id, int source_wagon, bool move_attached_wagons, int dest_vehicle_id, int dest_wagon)
+{
+ EnforcePrecondition(false, IsValidVehicle(source_vehicle_id) && source_wagon < GetNumWagons(source_vehicle_id));
+ EnforcePrecondition(false, dest_vehicle_id == -1 || (IsValidVehicle(dest_vehicle_id) && dest_wagon < GetNumWagons(dest_vehicle_id)));
+ EnforcePrecondition(false, ::GetVehicle(source_vehicle_id)->type == VEH_TRAIN);
+ EnforcePrecondition(false, dest_vehicle_id == -1 || ::GetVehicle(dest_vehicle_id)->type == VEH_TRAIN);
+
+ const Vehicle *v = ::GetVehicle(source_vehicle_id);
+ while (source_wagon-- > 0) v = GetNextUnit(v);
+ const Vehicle *w = NULL;
+ if (dest_vehicle_id != -1) {
+ w = ::GetVehicle(dest_vehicle_id);
+ while (dest_wagon-- > 0) w = GetNextUnit(w);
+ }
+
+ return AIObject::DoCommand(0, v->index | ((w == NULL ? INVALID_VEHICLE : w->index) << 16), move_attached_wagons ? 1 : 0, CMD_MOVE_RAIL_VEHICLE);
+}
+
+/* static */ int AIVehicle::GetRefitCapacity(VehicleID vehicle_id, CargoID cargo)
+{
+ if (!IsValidVehicle(vehicle_id)) return -1;
+ if (!AICargo::IsValidCargo(cargo)) return -1;
+
+ CommandCost res = ::DoCommand(0, vehicle_id, cargo, DC_QUERY_COST, GetCmdRefitVeh(::GetVehicle(vehicle_id)));
+ return CmdSucceeded(res) ? _returned_refit_capacity : -1;
+}
+
+/* static */ bool AIVehicle::RefitVehicle(VehicleID vehicle_id, CargoID cargo)
+{
+ EnforcePrecondition(false, IsValidVehicle(vehicle_id) && AICargo::IsValidCargo(cargo));
+
+ return AIObject::DoCommand(0, vehicle_id, cargo, GetCmdRefitVeh(::GetVehicle(vehicle_id)));
+}
+
+
+/* static */ bool AIVehicle::SellVehicle(VehicleID vehicle_id)
+{
+ EnforcePrecondition(false, IsValidVehicle(vehicle_id));
+
+ const Vehicle *v = ::GetVehicle(vehicle_id);
+ return AIObject::DoCommand(0, vehicle_id, v->type == VEH_TRAIN ? 1 : 0, GetCmdSellVeh(v));
+}
+
+/* static */ bool AIVehicle::SellWagon(VehicleID vehicle_id, int wagon, bool sell_attached_wagons)
+{
+ EnforcePrecondition(false, IsValidVehicle(vehicle_id) && wagon < GetNumWagons(vehicle_id));
+ EnforcePrecondition(false, ::GetVehicle(vehicle_id)->type == VEH_TRAIN);
+
+ const Vehicle *v = ::GetVehicle(vehicle_id);
+ while (wagon-- > 0) v = GetNextUnit(v);
+
+ return AIObject::DoCommand(0, v->index, sell_attached_wagons ? 1 : 0, CMD_SELL_RAIL_WAGON);
+}
+
+/* static */ bool AIVehicle::SendVehicleToDepot(VehicleID vehicle_id)
+{
+ EnforcePrecondition(false, IsValidVehicle(vehicle_id));
+
+ return AIObject::DoCommand(0, vehicle_id, 0, GetCmdSendToDepot(::GetVehicle(vehicle_id)));
+}
+
+/* static */ bool AIVehicle::IsInDepot(VehicleID vehicle_id)
+{
+ if (!IsValidVehicle(vehicle_id)) return false;
+ return ::GetVehicle(vehicle_id)->IsInDepot();
+}
+
+/* static */ bool AIVehicle::IsStoppedInDepot(VehicleID vehicle_id)
+{
+ if (!IsValidVehicle(vehicle_id)) return false;
+ return ::GetVehicle(vehicle_id)->IsStoppedInDepot();
+}
+
+/* static */ bool AIVehicle::StartStopVehicle(VehicleID vehicle_id)
+{
+ EnforcePrecondition(false, IsValidVehicle(vehicle_id));
+
+ return AIObject::DoCommand(0, vehicle_id, 0, CMD_START_STOP_VEHICLE);
+}
+
+/* static */ bool AIVehicle::SkipToVehicleOrder(VehicleID vehicle_id, AIOrder::OrderPosition order_position)
+{
+ order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position);
+
+ EnforcePrecondition(false, AIOrder::IsValidVehicleOrder(vehicle_id, order_position));
+
+ return AIObject::DoCommand(0, vehicle_id, order_position, CMD_SKIP_TO_ORDER);
+}
+
+/* static */ bool AIVehicle::ReverseVehicle(VehicleID vehicle_id)
+{
+ EnforcePrecondition(false, IsValidVehicle(vehicle_id));
+ EnforcePrecondition(false, ::GetVehicle(vehicle_id)->type == VEH_ROAD || ::GetVehicle(vehicle_id)->type == VEH_TRAIN);
+
+ switch (::GetVehicle(vehicle_id)->type) {
+ case VEH_ROAD: return AIObject::DoCommand(0, vehicle_id, 0, CMD_TURN_ROADVEH);
+ case VEH_TRAIN: return AIObject::DoCommand(0, vehicle_id, 0, CMD_REVERSE_TRAIN_DIRECTION);
+ default: NOT_REACHED();
+ }
+}
+
+/* static */ bool AIVehicle::SetName(VehicleID vehicle_id, const char *name)
+{
+ EnforcePrecondition(false, IsValidVehicle(vehicle_id));
+ EnforcePrecondition(false, !::StrEmpty(name));
+ EnforcePreconditionCustomError(false, ::strlen(name) < MAX_LENGTH_VEHICLE_NAME_BYTES, AIError::ERR_PRECONDITION_STRING_TOO_LONG);
+
+ return AIObject::DoCommand(0, vehicle_id, 0, CMD_RENAME_VEHICLE, name);
+}
+
+/* static */ TileIndex AIVehicle::GetLocation(VehicleID vehicle_id)
+{
+ if (!IsValidVehicle(vehicle_id)) return INVALID_TILE;
+
+ const Vehicle *v = ::GetVehicle(vehicle_id);
+ if (v->type == VEH_AIRCRAFT) {
+ uint x = Clamp(v->x_pos / TILE_SIZE, 0, ::MapSizeX() - 2);
+ uint y = Clamp(v->y_pos / TILE_SIZE, 0, ::MapSizeY() - 2);
+ return ::TileXY(x, y);
+ }
+
+ return v->tile;
+}
+
+/* static */ EngineID AIVehicle::GetEngineType(VehicleID vehicle_id)
+{
+ if (!IsValidVehicle(vehicle_id)) return INVALID_ENGINE;
+
+ return ::GetVehicle(vehicle_id)->engine_type;
+}
+
+/* static */ EngineID AIVehicle::GetWagonEngineType(VehicleID vehicle_id, int wagon)
+{
+ if (!IsValidVehicle(vehicle_id)) return INVALID_ENGINE;
+ if (wagon >= GetNumWagons(vehicle_id)) return INVALID_ENGINE;
+
+ const Vehicle *v = ::GetVehicle(vehicle_id);
+ if (v->type == VEH_TRAIN) {
+ while (wagon-- > 0) v = GetNextUnit(v);
+ }
+ return v->engine_type;
+}
+
+/* static */ int32 AIVehicle::GetUnitNumber(VehicleID vehicle_id)
+{
+ if (!IsValidVehicle(vehicle_id)) return -1;
+
+ return ::GetVehicle(vehicle_id)->unitnumber;
+}
+
+/* static */ const char *AIVehicle::GetName(VehicleID vehicle_id)
+{
+ if (!IsValidVehicle(vehicle_id)) return NULL;
+
+ static const int len = 64;
+ char *vehicle_name = MallocT<char>(len);
+
+ ::SetDParam(0, vehicle_id);
+ ::GetString(vehicle_name, STR_VEHICLE_NAME, &vehicle_name[len - 1]);
+ return vehicle_name;
+}
+
+/* static */ int32 AIVehicle::GetAge(VehicleID vehicle_id)
+{
+ if (!IsValidVehicle(vehicle_id)) return -1;
+
+ return ::GetVehicle(vehicle_id)->age;
+}
+
+/* static */ int32 AIVehicle::GetWagonAge(VehicleID vehicle_id, int wagon)
+{
+ if (!IsValidVehicle(vehicle_id)) return -1;
+ if (wagon >= GetNumWagons(vehicle_id)) return -1;
+
+ const Vehicle *v = ::GetVehicle(vehicle_id);
+ if (v->type == VEH_TRAIN) {
+ while (wagon-- > 0) v = GetNextUnit(v);
+ }
+ return v->age;
+}
+
+/* static */ int32 AIVehicle::GetMaxAge(VehicleID vehicle_id)
+{
+ if (!IsValidVehicle(vehicle_id)) return -1;
+
+ return ::GetVehicle(vehicle_id)->max_age;
+}
+
+/* static */ int32 AIVehicle::GetAgeLeft(VehicleID vehicle_id)
+{
+ if (!IsValidVehicle(vehicle_id)) return -1;
+
+ return ::GetVehicle(vehicle_id)->max_age - ::GetVehicle(vehicle_id)->age;
+}
+
+/* static */ int32 AIVehicle::GetCurrentSpeed(VehicleID vehicle_id)
+{
+ if (!IsValidVehicle(vehicle_id)) return -1;
+
+ return ::GetVehicle(vehicle_id)->GetDisplaySpeed();
+}
+
+/* static */ AIVehicle::VehicleState AIVehicle::GetState(VehicleID vehicle_id)
+{
+ if (!IsValidVehicle(vehicle_id)) return AIVehicle::VS_INVALID;
+
+ const Vehicle *v = ::GetVehicle(vehicle_id);
+ byte vehstatus = v->vehstatus;
+
+ if (vehstatus & ::VS_CRASHED) return AIVehicle::VS_CRASHED;
+ if (v->breakdown_ctr != 0) return AIVehicle::VS_BROKEN;
+ if (v->IsStoppedInDepot()) return AIVehicle::VS_IN_DEPOT;
+ if (vehstatus & ::VS_STOPPED) return AIVehicle::VS_STOPPED;
+ if (v->current_order.IsType(OT_LOADING)) return AIVehicle::VS_AT_STATION;
+ return AIVehicle::VS_RUNNING;
+}
+
+/* static */ Money AIVehicle::GetRunningCost(VehicleID vehicle_id)
+{
+ if (!IsValidVehicle(vehicle_id)) return -1;
+
+ return ::GetVehicle(vehicle_id)->GetRunningCost() >> 8;
+}
+
+/* static */ Money AIVehicle::GetProfitThisYear(VehicleID vehicle_id)
+{
+ if (!IsValidVehicle(vehicle_id)) return -1;
+
+ return ::GetVehicle(vehicle_id)->GetDisplayProfitThisYear();
+}
+
+/* static */ Money AIVehicle::GetProfitLastYear(VehicleID vehicle_id)
+{
+ if (!IsValidVehicle(vehicle_id)) return -1;
+
+ return ::GetVehicle(vehicle_id)->GetDisplayProfitLastYear();
+}
+
+/* static */ Money AIVehicle::GetCurrentValue(VehicleID vehicle_id)
+{
+ if (!IsValidVehicle(vehicle_id)) return -1;
+
+ return ::GetVehicle(vehicle_id)->value;
+}
+
+/* static */ AIVehicle::VehicleType AIVehicle::GetVehicleType(VehicleID vehicle_id)
+{
+ if (!IsValidVehicle(vehicle_id)) return VEHICLE_INVALID;
+
+ switch (::GetVehicle(vehicle_id)->type) {
+ case VEH_ROAD: return VEHICLE_ROAD;
+ case VEH_TRAIN: return VEHICLE_RAIL;
+ case VEH_SHIP: return VEHICLE_WATER;
+ case VEH_AIRCRAFT: return VEHICLE_AIR;
+ default: return VEHICLE_INVALID;
+ }
+}
+
+/* static */ AIRoad::RoadType AIVehicle::GetRoadType(VehicleID vehicle_id)
+{
+ if (!IsValidVehicle(vehicle_id)) return AIRoad::ROADTYPE_INVALID;
+ if (GetVehicleType(vehicle_id) != VEHICLE_ROAD) return AIRoad::ROADTYPE_INVALID;
+
+ return (AIRoad::RoadType)::GetVehicle(vehicle_id)->u.road.roadtype;
+}
+
+/* static */ int32 AIVehicle::GetCapacity(VehicleID vehicle_id, CargoID cargo)
+{
+ if (!IsValidVehicle(vehicle_id)) return -1;
+ if (!AICargo::IsValidCargo(cargo)) return -1;
+
+ uint32 amount = 0;
+ for (const Vehicle *v = ::GetVehicle(vehicle_id); v != NULL; v = v->Next()) {
+ if (v->cargo_type == cargo) amount += v->cargo_cap;
+ }
+
+ return amount;
+}
+
+/* static */ int32 AIVehicle::GetCargoLoad(VehicleID vehicle_id, CargoID cargo)
+{
+ if (!IsValidVehicle(vehicle_id)) return -1;
+ if (!AICargo::IsValidCargo(cargo)) return -1;
+
+ uint32 amount = 0;
+ for (const Vehicle *v = ::GetVehicle(vehicle_id); v != NULL; v = v->Next()) {
+ if (v->cargo_type == cargo) amount += v->cargo.Count();
+ }
+
+ return amount;
+}
+
+/* static */ GroupID AIVehicle::GetGroupID(VehicleID vehicle_id)
+{
+ if (!IsValidVehicle(vehicle_id)) return ::INVALID_GROUP;
+
+ return ::GetVehicle(vehicle_id)->group_id;
+}
+
+/* static */ bool AIVehicle::IsArticulated(VehicleID vehicle_id)
+{
+ if (!IsValidVehicle(vehicle_id)) return false;
+ if (GetVehicleType(vehicle_id) != VEHICLE_ROAD && GetVehicleType(vehicle_id) != VEHICLE_RAIL) return false;
+
+ const Vehicle *v = ::GetVehicle(vehicle_id);
+ switch (v->type) {
+ case VEH_ROAD: return RoadVehHasArticPart(v);
+ case VEH_TRAIN: return EngineHasArticPart(v);
+ default: NOT_REACHED();
+ }
+}
diff --git a/src/ai/api/ai_vehicle.hpp b/src/ai/api/ai_vehicle.hpp
new file mode 100644
index 000000000..8120fe947
--- /dev/null
+++ b/src/ai/api/ai_vehicle.hpp
@@ -0,0 +1,477 @@
+/* $Id$ */
+
+/** @file ai_vehicle.hpp Everything to query and build vehicles. */
+
+#ifndef AI_VEHICLE_HPP
+#define AI_VEHICLE_HPP
+
+#include "ai_object.hpp"
+#include "ai_error.hpp"
+#include "ai_road.hpp"
+#include "ai_order.hpp"
+
+/**
+ * Class that handles all vehicle related functions.
+ */
+class AIVehicle : public AIObject {
+public:
+ static const char *GetClassName() { return "AIVehicle"; }
+
+ /**
+ * All vehicle related error messages.
+ */
+ enum ErrorMessages {
+ /** Base for vehicle related errors */
+ ERR_VEHICLE_BASE = AIError::ERR_CAT_VEHICLE << AIError::ERR_CAT_BIT_SIZE,
+
+ /** Too many vehicles in the game, can't build any more. */
+ ERR_VEHICLE_TOO_MANY, // [STR_00E1_TOO_MANY_VEHICLES_IN_GAME]
+
+ /** Vehicle is not available */
+ ERR_VEHICLE_NOT_AVAILABLE, // [STR_AIRCRAFT_NOT_AVAILABLE, STR_ROAD_VEHICLE_NOT_AVAILABLE, STR_SHIP_NOT_AVAILABLE, STR_RAIL_VEHICLE_NOT_AVAILABLE]
+
+ /** Vehicle can't be build due to game settigns */
+ ERR_VEHICLE_BUILD_DISABLED, // [STR_A008_CAN_T_BUILD_AIRCRAFT, STR_980D_CAN_T_BUILD_SHIP, STR_9009_CAN_T_BUILD_ROAD_VEHICLE, STR_882B_CAN_T_BUILD_RAILROAD_VEHICLE]
+
+ /** Vehicle can't be build in the selected depot */
+ ERR_VEHICLE_WRONG_DEPOT, // [STR_DEPOT_WRONG_DEPOT_TYPE]
+
+ /** Vehicle can't return to the depot */
+ ERR_VEHICLE_CANNOT_SEND_TO_DEPOT, // [STR_8830_CAN_T_SEND_TRAIN_TO_DEPOT, STR_9018_CAN_T_SEND_VEHICLE_TO_DEPOT, STR_9819_CAN_T_SEND_SHIP_TO_DEPOT, STR_A012_CAN_T_SEND_AIRCRAFT_TO]
+
+ /** Vehicle can't start / stop */
+ ERR_VEHICLE_CANNOT_START_STOP, // [STR_883B_CAN_T_STOP_START_TRAIN, STR_9015_CAN_T_STOP_START_ROAD_VEHICLE, STR_9818_CAN_T_STOP_START_SHIP, STR_A016_CAN_T_STOP_START_AIRCRAFT]
+
+ /** Vehicle can't turn */
+ ERR_VEHICLE_CANNOT_TURN, // [STR_8869_CAN_T_REVERSE_DIRECTION, STR_9033_CAN_T_MAKE_VEHICLE_TURN]
+
+ /** Vehicle can't be refit */
+ ERR_VEHICLE_CANNOT_REFIT, // [STR_RAIL_CAN_T_REFIT_VEHICLE, STR_REFIT_ROAD_VEHICLE_CAN_T, STR_9841_CAN_T_REFIT_SHIP, STR_A042_CAN_T_REFIT_AIRCRAFT]
+
+ /** Vehicle is destroyed */
+ ERR_VEHICLE_IS_DESTROYED, // [STR_CAN_T_REFIT_DESTROYED_VEHICLE, STR_CAN_T_SELL_DESTROYED_VEHICLE]
+
+ /** Vehicle is not in a depot */
+ ERR_VEHICLE_NOT_IN_DEPOT, // [STR_A01B_AIRCRAFT_MUST_BE_STOPPED, STR_9013_MUST_BE_STOPPED_INSIDE, STR_TRAIN_MUST_BE_STOPPED, STR_980B_SHIP_MUST_BE_STOPPED_IN]
+
+ /** Vehicle is flying */
+ ERR_VEHICLE_IN_FLIGHT, // [STR_A017_AIRCRAFT_IS_IN_FLIGHT]
+
+ /** Vehicle is without power */
+ ERR_VEHCILE_NO_POWER, // [STR_TRAIN_START_NO_CATENARY]
+
+ };
+
+ /**
+ * The type of a vehicle available in the game. Trams for example are
+ * road vehicles, as maglev is a rail vehicle.
+ */
+ enum VehicleType {
+ /* Order IS important, as it now matches the internal state of the game for vehicle type */
+ VEHICLE_RAIL, //!< Rail type vehicle.
+ VEHICLE_ROAD, //!< Road type vehicle (bus / truck).
+ VEHICLE_WATER, //!< Water type vehicle.
+ VEHICLE_AIR, //!< Air type vehicle.
+ VEHICLE_INVALID = 0xFF, //!< Invalid vehicle type.
+ };
+
+ /**
+ * The different states a vehicle can be in.
+ */
+ enum VehicleState {
+ VS_RUNNING, //!< The vehicle is currently running.
+ VS_STOPPED, //!< The vehicle is stopped manually.
+ VS_IN_DEPOT, //!< The vehicle is stopped in the depot.
+ VS_AT_STATION, //!< The vehicle is stopped at a station and is currently loading or unloading.
+ VS_BROKEN, //!< The vehicle has broken down and will start running again in a while.
+ VS_CRASHED, //!< The vehicle is crashed (and will never run again).
+
+ VS_INVALID = 0xFF, //!< An invalid vehicle state.
+ };
+
+ /**
+ * Checks whether the given vehicle is valid and owned by you.
+ * @param vehicle_id The vehicle to check.
+ * @return True if and only if the vehicle is valid.
+ */
+ static bool IsValidVehicle(VehicleID vehicle_id);
+
+ /**
+ * Get the number of wagons a vehicle has.
+ * @param vehicle_id The vehicle to get the number of wagons from.
+ * @pre IsValidVehicle(vehicle_id).
+ * @return The number of wagons the vehicle has.
+ */
+ static int32 GetNumWagons(VehicleID vehicle_id);
+
+ /**
+ * Set the name of a vehicle.
+ * @param vehicle_id The vehicle to set the name for.
+ * @param name The name for the vehicle.
+ * @pre IsValidVehicle(vehicle_id).
+ * @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 and only if the name was changed.
+ */
+ static bool SetName(VehicleID vehicle_id, const char *name);
+
+ /**
+ * Get the name of a vehicle.
+ * @param vehicle_id The vehicle to get the name of.
+ * @pre IsValidVehicle(vehicle_id).
+ * @return The name the vehicle has.
+ */
+ static const char *GetName(VehicleID vehicle_id);
+
+ /**
+ * Get the current location of a vehicle.
+ * @param vehicle_id The vehicle to get the location of.
+ * @pre IsValidVehicle(vehicle_id).
+ * @return The tile the vehicle is currently on.
+ */
+ static TileIndex GetLocation(VehicleID vehicle_id);
+
+ /**
+ * Get the engine-type of a vehicle.
+ * @param vehicle_id The vehicle to get the engine-type of.
+ * @pre IsValidVehicle(vehicle_id).
+ * @return The engine type the vehicle has.
+ */
+ static EngineID GetEngineType(VehicleID vehicle_id);
+
+ /**
+ * Get the engine-type of a wagon.
+ * @param vehicle_id The vehicle to get the engine-type of.
+ * @param wagon The wagon in the vehicle to get the engine-type of.
+ * @pre IsValidVehicle(vehicle_id).
+ * @pre wagon < GetNumWagons(vehicle_id).
+ * @return The engine type the vehicle has.
+ */
+ static EngineID GetWagonEngineType(VehicleID vehicle_id, int wagon);
+
+ /**
+ * Get the unitnumber of a vehicle.
+ * @param vehicle_id The vehicle to get the unitnumber of.
+ * @pre IsValidVehicle(vehicle_id).
+ * @return The unitnumber the vehicle has.
+ */
+ static int32 GetUnitNumber(VehicleID vehicle_id);
+
+ /**
+ * Get the current age of a vehicle.
+ * @param vehicle_id The vehicle to get the age of.
+ * @pre IsValidVehicle(vehicle_id).
+ * @return The current age the vehicle has.
+ * @note The age is in days.
+ */
+ static int32 GetAge(VehicleID vehicle_id);
+
+ /**
+ * Get the current age of a second (or third, etc.) engine in a train vehicle.
+ * @param vehicle_id The vehicle to get the age of.
+ * @param wagon The wagon in the vehicle to get the age of.
+ * @pre IsValidVehicle(vehicle_id).
+ * @pre wagon < GetNumWagons(vehicle_id).
+ * @return The current age the vehicle has.
+ * @note The age is in days.
+ */
+ static int32 GetWagonAge(VehicleID vehicle_id, int wagon);
+
+ /**
+ * Get the maximum age of a vehicle.
+ * @param vehicle_id The vehicle to get the age of.
+ * @pre IsValidVehicle(vehicle_id).
+ * @return The maximum age the vehicle has.
+ * @note The age is in days.
+ */
+ static int32 GetMaxAge(VehicleID vehicle_id);
+
+ /**
+ * Get the age a vehicle has left (maximum - current).
+ * @param vehicle_id The vehicle to get the age of.
+ * @pre IsValidVehicle(vehicle_id).
+ * @return The age the vehicle has left.
+ * @note The age is in days.
+ */
+ static int32 GetAgeLeft(VehicleID vehicle_id);
+
+ /**
+ * Get the current speed of a vehicle.
+ * @param vehicle_id The vehicle to get the age of.
+ * @pre IsValidVehicle(vehicle_id).
+ * @return The current speed of the vehicle.
+ * @note Speed is in km/h.
+ */
+ static int32 GetCurrentSpeed(VehicleID vehicle_id);
+
+ /**
+ * Get the current state of a vehicle.
+ * @param vehicle_id The vehicle to get the state of.
+ * @pre IsValidVehicle(vehicle_id).
+ * @return The current state of the vehicle.
+ */
+ static VehicleState GetState(VehicleID vehicle_id);
+
+ /**
+ * Get the running cost of this vehicle.
+ * @param vehicle_id The vehicle to get the age of.
+ * @pre IsValidVehicle(vehicle_id).
+ * @return The running cost of the vehicle per year.
+ * @note Cost is per year; divide by 364 to get per day.
+ * @note This is not equal to AIEngine::GetRunningCost for Trains, because
+ * wagons and second engines can add up in the calculation too.
+ */
+ static Money GetRunningCost(VehicleID vehicle_id);
+
+ /**
+ * Get the current profit of a vehicle.
+ * @param vehicle_id The vehicle to get the profit of.
+ * @pre IsValidVehicle(vehicle_id).
+ * @return The current profit the vehicle has.
+ */
+ static Money GetProfitThisYear(VehicleID vehicle_id);
+
+ /**
+ * Get the profit of last year of a vehicle.
+ * @param vehicle_id The vehicle to get the profit of.
+ * @pre IsValidVehicle(vehicle_id).
+ * @return The profit the vehicle had last year.
+ */
+ static Money GetProfitLastYear(VehicleID vehicle_id);
+
+
+ /**
+ * Get the current value of a vehicle.
+ * @param vehicle_id The vehicle to get the value of.
+ * @pre IsValidVehicle(vehicle_id).
+ * @return The value the vehicle currently has (the amount you should get
+ * when you would sell the vehicle right now).
+ */
+ static Money GetCurrentValue(VehicleID vehicle_id);
+
+ /**
+ * Get the type of vehicle.
+ * @param vehicle_id The vehicle to get the type of.
+ * @pre IsValidVehicle(vehicle_id).
+ * @return The vehicle type.
+ */
+ static AIVehicle::VehicleType GetVehicleType(VehicleID vehicle_id);
+
+ /**
+ * Get the RoadType of the vehicle.
+ * @param vehicle_id The vehicle to get the RoadType of.
+ * @pre IsValidVehicle(vehicle_id).
+ * @pre GetVehicleType(vehicle_id) == VEHICLE_ROAD.
+ * @return The RoadType the vehicle has.
+ */
+ static AIRoad::RoadType GetRoadType(VehicleID vehicle_id);
+
+ /**
+ * Check if a vehicle is in a depot.
+ * @param vehicle_id The vehicle to check.
+ * @pre IsValidVehicle(vehicle_id).
+ * @return True if and only if the vehicle is in a depot.
+ */
+ static bool IsInDepot(VehicleID vehicle_id);
+
+ /**
+ * Check if a vehicle is in a depot and stopped.
+ * @param vehicle_id The vehicle to check.
+ * @pre IsValidVehicle(vehicle_id).
+ * @return True if and only if the vehicle is in a depot and stopped.
+ */
+ static bool IsStoppedInDepot(VehicleID vehicle_id);
+
+ /**
+ * Builds a vehicle with the given engine at the given depot.
+ * @param depot The depot where the vehicle will be build.
+ * @param engine_id The engine to use for this vehicle.
+ * @pre The tile at depot has a depot that can build the engine and
+ * is owned by you.
+ * @pre IsValidEngine(engine_id).
+ * @exception AIVehicle::ERR_VEHICLE_TOO_MANY
+ * @exception AIVehicle::ERR_VEHICLE_BUILD_DISABLED
+ * @exception AIVehicle::ERR_VEHICLE_WRONG_DEPOT
+ * @return The VehicleID of the new vehicle, or an invalid VehicleID when
+ * it failed. Check the return value using IsValidVehicle. In test-mode
+ * 0 is returned if it was successful; any other value indicates failure.
+ * @note In Test Mode it means you can't assign orders yet to this vehicle,
+ * as the vehicle isn't really built yet. Build it for real first before
+ * assigning orders.
+ */
+ static VehicleID BuildVehicle(TileIndex depot, EngineID engine_id);
+
+ /**
+ * Clones a vehicle at the given depot, copying or cloning it's orders.
+ * @param depot The depot where the vehicle will be build.
+ * @param vehicle_id The vehicle to use as example for the new vehicle.
+ * @param share_orders Should the orders be copied or shared?
+ * @pre The tile 'depot' has a depot on it, allowing 'vehicle_id'-type vehicles.
+ * @pre IsValidVehicle(vehicle_id).
+ * @exception AIVehicle::ERR_VEHICLE_TOO_MANY
+ * @exception AIVehicle::ERR_VEHICLE_BUILD_DISABLED
+ * @exception AIVehicle::ERR_VEHICLE_WRONG_DEPOT
+ * @return The VehicleID of the new vehicle, or an invalid VehicleID when
+ * it failed. Check the return value using IsValidVehicle. In test-mode
+ * 0 is returned if it was successful; any other value indicates failure.
+ */
+ static VehicleID CloneVehicle(TileIndex depot, VehicleID vehicle_id, bool share_orders);
+
+ /**
+ * Move a wagon after another wagon.
+ * @param source_vehicle_id The vehicle to move a wagon away from.
+ * @param source_wagon The wagon in source_vehicle to move.
+ * @param move_attached_wagons Also move all wagons attached to the wagon to move.
+ * @param dest_vehicle_id The vehicle to move the wagon to, or -1 to create a new vehicle.
+ * @param dest_wagon The wagon in dest_vehicle to place source_wagon after.
+ * @pre IsValidVehicle(source_vehicle_id).
+ * @pre source_wagon < GetNumWagons(source_vehicle_id).
+ * @pre dest_vehicle_id == -1 || (IsValidVehicle(dest_vehicle_id) && dest_wagon < GetNumWagons(dest_vehicle_id)).
+ * @pre GetVehicleType(source_vehicle_id) == VEHICLE_RAIL.
+ * @pre dest_vehicle_id == -1 || GetVehicleType(dest_vehicle_id) == VEHICLE_RAIL.
+ * @return Whether or not moving the wagon(s) succeeded.
+ */
+ static bool MoveWagon(VehicleID source_vehicle_id, int source_wagon, bool move_attached_wagons, int dest_vehicle_id, int dest_wagon);
+
+ /**
+ * Gets the capacity of the given vehicle when refited to the given cargo type.
+ * @param vehicle_id The vehicle to refit.
+ * @param cargo The cargo to refit to.
+ * @pre IsValidVehicle(vehicle_id).
+ * @pre AICargo::IsValidCargo(cargo).
+ * @pre You must own the vehicle.
+ * @pre The vehicle must be stopped in the depot.
+ * @return The capacity the vehicle will have when refited.
+ */
+ static int GetRefitCapacity(VehicleID vehicle_id, CargoID cargo);
+
+ /**
+ * Refits a vehicle to the given cargo type.
+ * @param vehicle_id The vehicle to refit.
+ * @param cargo The cargo to refit to.
+ * @pre IsValidVehicle(vehicle_id).
+ * @pre AICargo::IsValidCargo(cargo).
+ * @pre You must own the vehicle.
+ * @pre The vehicle must be stopped in the depot.
+ * @exception AIVehicle::ERR_VEHICLE_CANNOT_REFIT
+ * @exception AIVehicle::ERR_VEHICLE_IS_DESTROYED
+ * @exception AIVehicle::ERR_VEHICLE_NOT_IN_DEPOT
+ * @return True if and only if the refit succeeded.
+ */
+ static bool RefitVehicle(VehicleID vehicle_id, CargoID cargo);
+
+ /**
+ * Sells the given vehicle.
+ * @param vehicle_id The vehicle to sell.
+ * @pre IsValidVehicle(vehicle_id).
+ * @pre You must own the vehicle.
+ * @pre The vehicle must be stopped in the depot.
+ * @exception AIVehicle::ERR_VEHICLE_IS_DESTROYED
+ * @exception AIVehicle::ERR_VEHICLE_NOT_IN_DEPOT
+ * @return True if and only if the vehicle has been sold.
+ */
+ static bool SellVehicle(VehicleID vehicle_id);
+
+ /**
+ * Sells the given wagon from the vehicle.
+ * @param vehicle_id The vehicle to sell a wagon from.
+ * @param wagon The wagon to sell.
+ * @param sell_attached_wagons Sell all wagons attached to the one we want to sell.
+ * @pre IsValidVehicle(vehicle_id).
+ * @pre wagon < GetNumWagons(vehicle_id).
+ * @pre You must own the vehicle.
+ * @pre The vehicle must be stopped in the depot.
+ * @exception AIVehicle::ERR_VEHICLE_IS_DESTROYED
+ * @exception AIVehicle::ERR_VEHICLE_NOT_IN_DEPOT
+ * @return True if and only if the wagon(s) has been sold.
+ */
+ static bool SellWagon(VehicleID vehicle_id, int wagon, bool sell_attached_wagons);
+
+ /**
+ * Sends the given vehicle to a depot.
+ * @param vehicle_id The vehicle to send to a depot.
+ * @pre IsValidVehicle(vehicle_id).
+ * @exception AIVehicle::ERR_VEHICLE_CANNOT_SEND_TO_DEPOT
+ * @return True if and only if the vehicle has been sent to a depot.
+ */
+ static bool SendVehicleToDepot(VehicleID vehicle_id);
+
+ /**
+ * Starts or stops the given vehicle depending on the current state.
+ * @param vehicle_id The vehicle to start/stop.
+ * @pre IsValidVehicle(vehicle_id).
+ * @exception AIVehicle::ERR_VEHICLE_CANNOT_START_STOP
+ * @exception (For aircraft only): AIVehicle::ERR_VEHICLE_IN_FLIGHT
+ * @exception (For trains only): AIVehicle::ERR_VEHICLE_NO_POWER
+ * @return True if and only if the vehicle has been started or stopped.
+ */
+ static bool StartStopVehicle(VehicleID vehicle_id);
+
+ /**
+ * Skips the current order of the given vehicle.
+ * @param vehicle_id The vehicle to skip the order for.
+ * @param order_position The selected order to which we want to skip.
+ * @pre IsValidVehicleOrder(vehicle_id, order_position).
+ * @return True if and only if the order has been skipped.
+ */
+ static bool SkipToVehicleOrder(VehicleID vehicle_id, AIOrder::OrderPosition order_position);
+
+ /**
+ * Turn the given vehicle so it'll drive the other way.
+ * @param vehicle_id The vehicle to turn.
+ * @pre IsValidVehicle(vehicle_id).
+ * @pre GetVehicleType(vehicle_id) == VEHICLE_ROAD || GetVehicleType(vehicle_id) == VEHICLE_RAIL.
+ * @return True if and only if the vehicle has started to turn.
+ * @note Vehicles cannot always be reversed. For example busses and trucks need to be running
+ * and not be inside a depot.
+ */
+ static bool ReverseVehicle(VehicleID vehicle_id);
+
+ /**
+ * Get the maximum amount of a specific cargo the given vehicle can transport.
+ * @param vehicle_id The vehicle to get the capacity of.
+ * @param cargo The cargo to get the capacity for.
+ * @pre IsValidVehicle(vehicle_id).
+ * @pre AICargo::IsValidCargo(cargo).
+ * @return The maximum amount of the given cargo the vehicle can transport.
+ */
+ static int32 GetCapacity(VehicleID vehicle_id, CargoID cargo);
+
+ /**
+ * Get the length of a the total vehicle in 1/16's of a tile.
+ * @param vehicle_id The vehicle to get the length of.
+ * @pre IsValidVehicle(vehicle_id).
+ * @pre GetVehicleType(vehicle_id) == AIVehicle.VEHICLE_ROAD || GetVehicleType(vehicle_id) == AIVehicle.VEHICLE_RAIL.
+ * @return The length of the engine.
+ */
+ static int GetLength(VehicleID vehicle_id);
+
+ /**
+ * Get the amount of a specific cargo the given vehicle transports.
+ * @param vehicle_id The vehicle to get the load amount of.
+ * @param cargo The cargo to get the load amount for.
+ * @pre IsValidVehicle(vehicle_id).
+ * @pre AICargo::IsValidCargo(cargo).
+ * @return The amount of the given cargo the vehicle currently transports.
+ */
+ static int32 GetCargoLoad(VehicleID vehicle_id, CargoID cargo);
+
+ /**
+ * Get the group of a given vehicle.
+ * @param vehicle_id The vehicle to get the group from.
+ * @return The group of the given vehicle.
+ */
+ static GroupID GetGroupID(VehicleID vehicle_id);
+
+ /**
+ * Check if the vehicle is articulated.
+ * @param vehicle_id The vehicle to check.
+ * @pre IsValidVehicle(vehicle_id).
+ * @pre GetVehicleType(vehicle_id) == VEHICLE_ROAD || GetVehicleType(vehicle_id) == VEHICLE_RAIL.
+ * @return True if the vehicle is articulated.
+ */
+ static bool IsArticulated(VehicleID vehicle_id);
+};
+
+#endif /* AI_VEHICLE_HPP */
diff --git a/src/ai/api/ai_vehicle.hpp.sq b/src/ai/api/ai_vehicle.hpp.sq
new file mode 100644
index 000000000..87dce4893
--- /dev/null
+++ b/src/ai/api/ai_vehicle.hpp.sq
@@ -0,0 +1,141 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_vehicle.hpp"
+
+namespace SQConvert {
+ /* Allow enums to be used as Squirrel parameters */
+ template <> AIVehicle::ErrorMessages GetParam(ForceType<AIVehicle::ErrorMessages>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIVehicle::ErrorMessages)tmp; }
+ template <> int Return<AIVehicle::ErrorMessages>(HSQUIRRELVM vm, AIVehicle::ErrorMessages res) { sq_pushinteger(vm, (int32)res); return 1; }
+ template <> AIVehicle::VehicleType GetParam(ForceType<AIVehicle::VehicleType>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIVehicle::VehicleType)tmp; }
+ template <> int Return<AIVehicle::VehicleType>(HSQUIRRELVM vm, AIVehicle::VehicleType res) { sq_pushinteger(vm, (int32)res); return 1; }
+ template <> AIVehicle::VehicleState GetParam(ForceType<AIVehicle::VehicleState>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (AIVehicle::VehicleState)tmp; }
+ template <> int Return<AIVehicle::VehicleState>(HSQUIRRELVM vm, AIVehicle::VehicleState res) { sq_pushinteger(vm, (int32)res); return 1; }
+
+ /* Allow AIVehicle to be used as Squirrel parameter */
+ template <> AIVehicle *GetParam(ForceType<AIVehicle *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIVehicle *)instance; }
+ template <> AIVehicle &GetParam(ForceType<AIVehicle &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIVehicle *)instance; }
+ template <> const AIVehicle *GetParam(ForceType<const AIVehicle *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIVehicle *)instance; }
+ template <> const AIVehicle &GetParam(ForceType<const AIVehicle &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIVehicle *)instance; }
+ template <> int Return<AIVehicle *>(HSQUIRRELVM vm, AIVehicle *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIVehicle", res, NULL, DefSQDestructorCallback<AIVehicle>); return 1; }
+}; // namespace SQConvert
+
+void SQAIVehicle_Register(Squirrel *engine) {
+ DefSQClass <AIVehicle> SQAIVehicle("AIVehicle");
+ SQAIVehicle.PreRegister(engine);
+ SQAIVehicle.AddConstructor<void (AIVehicle::*)(), 1>(engine, "x");
+
+ SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_BASE, "ERR_VEHICLE_BASE");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_TOO_MANY, "ERR_VEHICLE_TOO_MANY");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_NOT_AVAILABLE, "ERR_VEHICLE_NOT_AVAILABLE");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_BUILD_DISABLED, "ERR_VEHICLE_BUILD_DISABLED");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_WRONG_DEPOT, "ERR_VEHICLE_WRONG_DEPOT");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_CANNOT_SEND_TO_DEPOT, "ERR_VEHICLE_CANNOT_SEND_TO_DEPOT");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_CANNOT_START_STOP, "ERR_VEHICLE_CANNOT_START_STOP");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_CANNOT_TURN, "ERR_VEHICLE_CANNOT_TURN");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_CANNOT_REFIT, "ERR_VEHICLE_CANNOT_REFIT");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_IS_DESTROYED, "ERR_VEHICLE_IS_DESTROYED");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_NOT_IN_DEPOT, "ERR_VEHICLE_NOT_IN_DEPOT");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHICLE_IN_FLIGHT, "ERR_VEHICLE_IN_FLIGHT");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::ERR_VEHCILE_NO_POWER, "ERR_VEHCILE_NO_POWER");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::VEHICLE_RAIL, "VEHICLE_RAIL");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::VEHICLE_ROAD, "VEHICLE_ROAD");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::VEHICLE_WATER, "VEHICLE_WATER");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::VEHICLE_AIR, "VEHICLE_AIR");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::VEHICLE_INVALID, "VEHICLE_INVALID");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::VS_RUNNING, "VS_RUNNING");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::VS_STOPPED, "VS_STOPPED");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::VS_IN_DEPOT, "VS_IN_DEPOT");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::VS_AT_STATION, "VS_AT_STATION");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::VS_BROKEN, "VS_BROKEN");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::VS_CRASHED, "VS_CRASHED");
+ SQAIVehicle.DefSQConst(engine, AIVehicle::VS_INVALID, "VS_INVALID");
+
+ AIError::RegisterErrorMap(STR_00E1_TOO_MANY_VEHICLES_IN_GAME, AIVehicle::ERR_VEHICLE_TOO_MANY);
+ AIError::RegisterErrorMap(STR_AIRCRAFT_NOT_AVAILABLE, AIVehicle::ERR_VEHICLE_NOT_AVAILABLE);
+ AIError::RegisterErrorMap(STR_ROAD_VEHICLE_NOT_AVAILABLE, AIVehicle::ERR_VEHICLE_NOT_AVAILABLE);
+ AIError::RegisterErrorMap(STR_SHIP_NOT_AVAILABLE, AIVehicle::ERR_VEHICLE_NOT_AVAILABLE);
+ AIError::RegisterErrorMap(STR_RAIL_VEHICLE_NOT_AVAILABLE, AIVehicle::ERR_VEHICLE_NOT_AVAILABLE);
+ AIError::RegisterErrorMap(STR_A008_CAN_T_BUILD_AIRCRAFT, AIVehicle::ERR_VEHICLE_BUILD_DISABLED);
+ AIError::RegisterErrorMap(STR_980D_CAN_T_BUILD_SHIP, AIVehicle::ERR_VEHICLE_BUILD_DISABLED);
+ AIError::RegisterErrorMap(STR_9009_CAN_T_BUILD_ROAD_VEHICLE, AIVehicle::ERR_VEHICLE_BUILD_DISABLED);
+ AIError::RegisterErrorMap(STR_882B_CAN_T_BUILD_RAILROAD_VEHICLE, AIVehicle::ERR_VEHICLE_BUILD_DISABLED);
+ AIError::RegisterErrorMap(STR_DEPOT_WRONG_DEPOT_TYPE, AIVehicle::ERR_VEHICLE_WRONG_DEPOT);
+ AIError::RegisterErrorMap(STR_8830_CAN_T_SEND_TRAIN_TO_DEPOT, AIVehicle::ERR_VEHICLE_CANNOT_SEND_TO_DEPOT);
+ AIError::RegisterErrorMap(STR_9018_CAN_T_SEND_VEHICLE_TO_DEPOT, AIVehicle::ERR_VEHICLE_CANNOT_SEND_TO_DEPOT);
+ AIError::RegisterErrorMap(STR_9819_CAN_T_SEND_SHIP_TO_DEPOT, AIVehicle::ERR_VEHICLE_CANNOT_SEND_TO_DEPOT);
+ AIError::RegisterErrorMap(STR_A012_CAN_T_SEND_AIRCRAFT_TO, AIVehicle::ERR_VEHICLE_CANNOT_SEND_TO_DEPOT);
+ AIError::RegisterErrorMap(STR_883B_CAN_T_STOP_START_TRAIN, AIVehicle::ERR_VEHICLE_CANNOT_START_STOP);
+ AIError::RegisterErrorMap(STR_9015_CAN_T_STOP_START_ROAD_VEHICLE, AIVehicle::ERR_VEHICLE_CANNOT_START_STOP);
+ AIError::RegisterErrorMap(STR_9818_CAN_T_STOP_START_SHIP, AIVehicle::ERR_VEHICLE_CANNOT_START_STOP);
+ AIError::RegisterErrorMap(STR_A016_CAN_T_STOP_START_AIRCRAFT, AIVehicle::ERR_VEHICLE_CANNOT_START_STOP);
+ AIError::RegisterErrorMap(STR_8869_CAN_T_REVERSE_DIRECTION, AIVehicle::ERR_VEHICLE_CANNOT_TURN);
+ AIError::RegisterErrorMap(STR_9033_CAN_T_MAKE_VEHICLE_TURN, AIVehicle::ERR_VEHICLE_CANNOT_TURN);
+ AIError::RegisterErrorMap(STR_RAIL_CAN_T_REFIT_VEHICLE, AIVehicle::ERR_VEHICLE_CANNOT_REFIT);
+ AIError::RegisterErrorMap(STR_REFIT_ROAD_VEHICLE_CAN_T, AIVehicle::ERR_VEHICLE_CANNOT_REFIT);
+ AIError::RegisterErrorMap(STR_9841_CAN_T_REFIT_SHIP, AIVehicle::ERR_VEHICLE_CANNOT_REFIT);
+ AIError::RegisterErrorMap(STR_A042_CAN_T_REFIT_AIRCRAFT, AIVehicle::ERR_VEHICLE_CANNOT_REFIT);
+ AIError::RegisterErrorMap(STR_CAN_T_REFIT_DESTROYED_VEHICLE, AIVehicle::ERR_VEHICLE_IS_DESTROYED);
+ AIError::RegisterErrorMap(STR_CAN_T_SELL_DESTROYED_VEHICLE, AIVehicle::ERR_VEHICLE_IS_DESTROYED);
+ AIError::RegisterErrorMap(STR_A01B_AIRCRAFT_MUST_BE_STOPPED, AIVehicle::ERR_VEHICLE_NOT_IN_DEPOT);
+ AIError::RegisterErrorMap(STR_9013_MUST_BE_STOPPED_INSIDE, AIVehicle::ERR_VEHICLE_NOT_IN_DEPOT);
+ AIError::RegisterErrorMap(STR_TRAIN_MUST_BE_STOPPED, AIVehicle::ERR_VEHICLE_NOT_IN_DEPOT);
+ AIError::RegisterErrorMap(STR_980B_SHIP_MUST_BE_STOPPED_IN, AIVehicle::ERR_VEHICLE_NOT_IN_DEPOT);
+ AIError::RegisterErrorMap(STR_A017_AIRCRAFT_IS_IN_FLIGHT, AIVehicle::ERR_VEHICLE_IN_FLIGHT);
+ AIError::RegisterErrorMap(STR_TRAIN_START_NO_CATENARY, AIVehicle::ERR_VEHCILE_NO_POWER);
+
+ AIError::RegisterErrorMapString(AIVehicle::ERR_VEHICLE_TOO_MANY, "ERR_VEHICLE_TOO_MANY");
+ AIError::RegisterErrorMapString(AIVehicle::ERR_VEHICLE_NOT_AVAILABLE, "ERR_VEHICLE_NOT_AVAILABLE");
+ AIError::RegisterErrorMapString(AIVehicle::ERR_VEHICLE_BUILD_DISABLED, "ERR_VEHICLE_BUILD_DISABLED");
+ AIError::RegisterErrorMapString(AIVehicle::ERR_VEHICLE_WRONG_DEPOT, "ERR_VEHICLE_WRONG_DEPOT");
+ AIError::RegisterErrorMapString(AIVehicle::ERR_VEHICLE_CANNOT_SEND_TO_DEPOT, "ERR_VEHICLE_CANNOT_SEND_TO_DEPOT");
+ AIError::RegisterErrorMapString(AIVehicle::ERR_VEHICLE_CANNOT_START_STOP, "ERR_VEHICLE_CANNOT_START_STOP");
+ AIError::RegisterErrorMapString(AIVehicle::ERR_VEHICLE_CANNOT_TURN, "ERR_VEHICLE_CANNOT_TURN");
+ AIError::RegisterErrorMapString(AIVehicle::ERR_VEHICLE_CANNOT_REFIT, "ERR_VEHICLE_CANNOT_REFIT");
+ AIError::RegisterErrorMapString(AIVehicle::ERR_VEHICLE_IS_DESTROYED, "ERR_VEHICLE_IS_DESTROYED");
+ AIError::RegisterErrorMapString(AIVehicle::ERR_VEHICLE_NOT_IN_DEPOT, "ERR_VEHICLE_NOT_IN_DEPOT");
+ AIError::RegisterErrorMapString(AIVehicle::ERR_VEHICLE_IN_FLIGHT, "ERR_VEHICLE_IN_FLIGHT");
+ AIError::RegisterErrorMapString(AIVehicle::ERR_VEHCILE_NO_POWER, "ERR_VEHCILE_NO_POWER");
+
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetClassName, "GetClassName", 1, "x");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::IsValidVehicle, "IsValidVehicle", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetNumWagons, "GetNumWagons", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::SetName, "SetName", 3, "xis");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetName, "GetName", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetLocation, "GetLocation", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetEngineType, "GetEngineType", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetWagonEngineType, "GetWagonEngineType", 3, "xii");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetUnitNumber, "GetUnitNumber", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetAge, "GetAge", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetWagonAge, "GetWagonAge", 3, "xii");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetMaxAge, "GetMaxAge", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetAgeLeft, "GetAgeLeft", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetCurrentSpeed, "GetCurrentSpeed", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetState, "GetState", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetRunningCost, "GetRunningCost", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetProfitThisYear, "GetProfitThisYear", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetProfitLastYear, "GetProfitLastYear", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetCurrentValue, "GetCurrentValue", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetVehicleType, "GetVehicleType", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetRoadType, "GetRoadType", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::IsInDepot, "IsInDepot", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::IsStoppedInDepot, "IsStoppedInDepot", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::BuildVehicle, "BuildVehicle", 3, "xii");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::CloneVehicle, "CloneVehicle", 4, "xiib");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::MoveWagon, "MoveWagon", 6, "xiibii");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetRefitCapacity, "GetRefitCapacity", 3, "xii");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::RefitVehicle, "RefitVehicle", 3, "xii");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::SellVehicle, "SellVehicle", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::SellWagon, "SellWagon", 4, "xiib");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::SendVehicleToDepot, "SendVehicleToDepot", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::StartStopVehicle, "StartStopVehicle", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::SkipToVehicleOrder, "SkipToVehicleOrder", 3, "xii");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::ReverseVehicle, "ReverseVehicle", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetCapacity, "GetCapacity", 3, "xii");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetLength, "GetLength", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetCargoLoad, "GetCargoLoad", 3, "xii");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::GetGroupID, "GetGroupID", 2, "xi");
+ SQAIVehicle.DefSQStaticMethod(engine, &AIVehicle::IsArticulated, "IsArticulated", 2, "xi");
+
+ SQAIVehicle.PostRegister(engine);
+}
diff --git a/src/ai/api/ai_vehiclelist.cpp b/src/ai/api/ai_vehiclelist.cpp
new file mode 100644
index 000000000..127ac77e9
--- /dev/null
+++ b/src/ai/api/ai_vehiclelist.cpp
@@ -0,0 +1,37 @@
+/* $Id$ */
+
+/** @file ai_vehiclelist.cpp Implementation of AIVehicleList and friends. */
+
+#include "ai_vehiclelist.hpp"
+#include "ai_station.hpp"
+#include "../../openttd.h"
+#include "../../company_func.h"
+#include "../../vehicle_base.h"
+
+AIVehicleList::AIVehicleList()
+{
+ Vehicle *v;
+ FOR_ALL_VEHICLES(v) {
+ if (v->owner == _current_company && v->IsPrimaryVehicle()) this->AddItem(v->index);
+ }
+}
+
+AIVehicleList_Station::AIVehicleList_Station(StationID station_id)
+{
+ if (!AIStation::IsValidStation(station_id)) return;
+
+ Vehicle *v;
+
+ FOR_ALL_VEHICLES(v) {
+ if (v->owner == _current_company && v->IsPrimaryVehicle()) {
+ const Order *order;
+
+ FOR_VEHICLE_ORDERS(v, order) {
+ if (order->IsType(OT_GOTO_STATION) && order->GetDestination() == station_id) {
+ this->AddItem(v->index);
+ break;
+ }
+ }
+ }
+ }
+}
diff --git a/src/ai/api/ai_vehiclelist.hpp b/src/ai/api/ai_vehiclelist.hpp
new file mode 100644
index 000000000..0e91d75bc
--- /dev/null
+++ b/src/ai/api/ai_vehiclelist.hpp
@@ -0,0 +1,34 @@
+/* $Id$ */
+
+/** @file ai_vehiclelist.hpp List all the vehicles (you own). */
+
+#ifndef AI_VEHICLELIST_HPP
+#define AI_VEHICLELIST_HPP
+
+#include "ai_abstractlist.hpp"
+
+/**
+ * Creates a list of vehicles of which you are the owner.
+ * @ingroup AIList
+ */
+class AIVehicleList : public AIAbstractList {
+public:
+ static const char *GetClassName() { return "AIVehicleList"; }
+ AIVehicleList();
+};
+
+/**
+ * Creates a list of vehicles that have orders to a given station.
+ * @ingroup AIList
+ */
+class AIVehicleList_Station : public AIAbstractList {
+public:
+ static const char *GetClassName() { return "AIVehicleList_Station"; }
+
+ /**
+ * @param station_id The station to get the list of vehicles that have orders to him from.
+ */
+ AIVehicleList_Station(StationID station_id);
+};
+
+#endif /* AI_VEHICLELIST_HPP */
diff --git a/src/ai/api/ai_vehiclelist.hpp.sq b/src/ai/api/ai_vehiclelist.hpp.sq
new file mode 100644
index 000000000..5a5f930cb
--- /dev/null
+++ b/src/ai/api/ai_vehiclelist.hpp.sq
@@ -0,0 +1,42 @@
+/* $Id$ */
+/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */
+
+#include "ai_vehiclelist.hpp"
+
+namespace SQConvert {
+ /* Allow AIVehicleList to be used as Squirrel parameter */
+ template <> AIVehicleList *GetParam(ForceType<AIVehicleList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIVehicleList *)instance; }
+ template <> AIVehicleList &GetParam(ForceType<AIVehicleList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIVehicleList *)instance; }
+ template <> const AIVehicleList *GetParam(ForceType<const AIVehicleList *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIVehicleList *)instance; }
+ template <> const AIVehicleList &GetParam(ForceType<const AIVehicleList &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIVehicleList *)instance; }
+ template <> int Return<AIVehicleList *>(HSQUIRRELVM vm, AIVehicleList *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIVehicleList", res, NULL, DefSQDestructorCallback<AIVehicleList>); return 1; }
+}; // namespace SQConvert
+
+void SQAIVehicleList_Register(Squirrel *engine) {
+ DefSQClass <AIVehicleList> SQAIVehicleList("AIVehicleList");
+ SQAIVehicleList.PreRegister(engine, "AIAbstractList");
+ SQAIVehicleList.AddConstructor<void (AIVehicleList::*)(), 1>(engine, "x");
+
+ SQAIVehicleList.DefSQStaticMethod(engine, &AIVehicleList::GetClassName, "GetClassName", 1, "x");
+
+ SQAIVehicleList.PostRegister(engine);
+}
+
+namespace SQConvert {
+ /* Allow AIVehicleList_Station to be used as Squirrel parameter */
+ template <> AIVehicleList_Station *GetParam(ForceType<AIVehicleList_Station *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIVehicleList_Station *)instance; }
+ template <> AIVehicleList_Station &GetParam(ForceType<AIVehicleList_Station &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIVehicleList_Station *)instance; }
+ template <> const AIVehicleList_Station *GetParam(ForceType<const AIVehicleList_Station *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (AIVehicleList_Station *)instance; }
+ template <> const AIVehicleList_Station &GetParam(ForceType<const AIVehicleList_Station &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(AIVehicleList_Station *)instance; }
+ template <> int Return<AIVehicleList_Station *>(HSQUIRRELVM vm, AIVehicleList_Station *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, "AIVehicleList_Station", res, NULL, DefSQDestructorCallback<AIVehicleList_Station>); return 1; }
+}; // namespace SQConvert
+
+void SQAIVehicleList_Station_Register(Squirrel *engine) {
+ DefSQClass <AIVehicleList_Station> SQAIVehicleList_Station("AIVehicleList_Station");
+ SQAIVehicleList_Station.PreRegister(engine, "AIAbstractList");
+ SQAIVehicleList_Station.AddConstructor<void (AIVehicleList_Station::*)(StationID station_id), 2>(engine, "xi");
+
+ SQAIVehicleList_Station.DefSQStaticMethod(engine, &AIVehicleList_Station::GetClassName, "GetClassName", 1, "x");
+
+ SQAIVehicleList_Station.PostRegister(engine);
+}
diff --git a/src/ai/api/squirrel_export.awk b/src/ai/api/squirrel_export.awk
new file mode 100644
index 000000000..877586722
--- /dev/null
+++ b/src/ai/api/squirrel_export.awk
@@ -0,0 +1,385 @@
+# $Id$
+#
+# Awk script to automatically generate the code needed
+# to export the AI API to Squirrel.
+#
+# Note that arrays are 1 based...
+#
+
+# Simple insertion sort.
+function array_sort(ARRAY, ELEMENTS, temp, i, j) {
+ for (i = 2; i <= ELEMENTS; i++)
+ for (j = i; ARRAY[j - 1] > ARRAY[j]; --j) {
+ temp = ARRAY[j]
+ ARRAY[j] = ARRAY[j - 1]
+ ARRAY[j - 1] = temp
+ }
+ return
+}
+
+function dump_class_templates(name) {
+ print " template <> " name " *GetParam(ForceType<" name " *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (" name " *)instance; }"
+ print " template <> " name " &GetParam(ForceType<" name " &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(" name " *)instance; }"
+ print " template <> const " name " *GetParam(ForceType<const " name " *>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return (" name " *)instance; }"
+ print " template <> const " name " &GetParam(ForceType<const " name " &>, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQUserPointer instance; sq_getinstanceup(vm, index, &instance, 0); return *(" name " *)instance; }"
+ if (name == "AIEvent") {
+ print " template <> int Return<" name " *>(HSQUIRRELVM vm, " name " *res) { if (res == NULL) { sq_pushnull(vm); return 1; } Squirrel::CreateClassInstanceVM(vm, \"" name "\", res, NULL, DefSQDestructorCallback<" name ">); return 1; }"
+ } else {
+ print " template <> int Return<" name " *>(HSQUIRRELVM vm, " name " *res) { if (res == NULL) { sq_pushnull(vm); return 1; } res->AddRef(); Squirrel::CreateClassInstanceVM(vm, \"" name "\", res, NULL, DefSQDestructorCallback<" name ">); return 1; }"
+ }
+}
+
+BEGIN {
+ enum_size = 0
+ enum_value_size = 0
+ enum_string_to_error_size = 0
+ enum_error_to_string_size = 0
+ struct_size = 0
+ method_size = 0
+ static_method_size = 0
+ virtual_class = "false"
+ super_cls = ""
+ cls = ""
+ start_squirrel_define_on_next_line = "false"
+ cls_level = 0
+ RS = "\r|\n"
+}
+
+/@file/ {
+ # Break it in two lines, so SVN doesn't replace it
+ printf "/* $I"
+ print "d$ */"
+ print "/* THIS FILE IS AUTO-GENERATED; PLEASE DO NOT ALTER MANUALLY */"
+ print ""
+ print "#include \"" $3 "\""
+}
+
+# Remove the old squirrel stuff
+/#ifdef DEFINE_SQUIRREL_CLASS/ { squirrel_stuff = "true"; next; }
+/^#endif \/\* DEFINE_SQUIRREL_CLASS \*\// { if (squirrel_stuff == "true") { squirrel_stuff = "false"; next; } }
+{ if (squirrel_stuff == "true") next; }
+
+# Ignore forward declarations of classes
+/^( *)class(.*);/ { next; }
+# We only want to have public functions exported for now
+/^( *)class/ {
+ if (cls_level == 0) {
+ public = "false"
+ cls_param[0] = ""
+ cls_param[1] = 1
+ cls_param[2] = "x"
+ cls = $2
+ if (match($4, "public") || match($4, "protected") || match($4, "private")) {
+ super_cls = $5
+ } else {
+ super_cls = $4
+ }
+ } else if (cls_level == 1) {
+ struct_size++
+ structs[struct_size] = cls "::" $2
+ }
+ cls_level++
+ next
+}
+/^( *)public/ { if (cls_level == 1) public = "true"; next; }
+/^( *)protected/ { if (cls_level == 1) public = "false"; next; }
+/^( *)private/ { if (cls_level == 1) public = "false"; next; }
+
+# Ignore special doxygen blocks
+/^#ifndef DOXYGEN_SKIP/ { doxygen_skip = "next"; next; }
+/^#ifdef DOXYGEN_SKIP/ { doxygen_skip = "true"; next; }
+/^#endif/ { doxygen_skip = "false"; next; }
+/^#else/ {
+ if (doxygen_skip == "next") {
+ doxygen_skip = "true";
+ } else {
+ doxygen_skip = "false";
+ }
+ next;
+}
+{ if (doxygen_skip == "true") next }
+
+# Ignore the comments
+/^#/ { next; }
+/\/\*.*\*\// { comment = "false"; next; }
+/\/\*/ { comment = "true"; next; }
+/\*\// { comment = "false"; next; }
+{ if (comment == "true") next }
+
+# We need to make specialized conversions for structs
+/^( *)struct/ {
+ cls_level++
+ if (public == "false") next
+ if (cls_level != 1) next
+ struct_size++
+ structs[struct_size] = cls "::" $2
+ next
+}
+
+# We need to make specialized conversions for enums
+/^( *)enum/ {
+ cls_level++
+ if (public == "false") next;
+ in_enum = "true"
+ enum_size++
+ enums[enum_size] = cls "::" $2
+ next
+}
+
+# Maybe the end of the class, if so we can start with the Squirrel export pretty soon
+/};/ {
+ cls_level--
+ if (cls_level != 0) {
+ in_enum = "false";
+ next;
+ }
+ if (cls == "") {
+ next;
+ }
+ start_squirrel_define_on_next_line = "true"
+ next;
+}
+
+# Empty/white lines. When we may do the Squirrel export, do that export.
+/^([ ]*)$/ {
+ if (start_squirrel_define_on_next_line == "false") next
+ spaces = " ";
+ public = "false"
+ namespace_opened = "false"
+
+ print ""
+
+ # First check whether we have enums to print
+ if (enum_size != 0) {
+ if (namespace_opened == "false") {
+ print "namespace SQConvert {"
+ namespace_opened = "true"
+ }
+ print " /* Allow enums to be used as Squirrel parameters */"
+ for (i = 1; i <= enum_size; i++) {
+ print " template <> " enums[i] " GetParam(ForceType<" enums[i] ">, HSQUIRRELVM vm, int index, SQAutoFreePointers *ptr) { SQInteger tmp; sq_getinteger(vm, index, &tmp); return (" enums[i] ")tmp; }"
+ print " template <> int Return<" enums[i] ">(HSQUIRRELVM vm, " enums[i] " res) { sq_pushinteger(vm, (int32)res); return 1; }"
+ delete enums[i]
+ }
+ }
+
+ # Then check whether we have structs/classes to print
+ if (struct_size != 0) {
+ if (namespace_opened == "false") {
+ print "namespace SQConvert {"
+ namespace_opened = "true"
+ }
+ print " /* Allow inner classes/structs to be used as Squirrel parameters */"
+ for (i = 1; i <= struct_size; i++) {
+ dump_class_templates(structs[i])
+ delete structs[i]
+ }
+ }
+
+ if (namespace_opened == "false") {
+ print "namespace SQConvert {"
+ namespace_opened = "true"
+ } else {
+ print ""
+ }
+ print " /* Allow " cls " to be used as Squirrel parameter */"
+ dump_class_templates(cls)
+
+ print "}; // namespace SQConvert"
+
+ print "";
+ # Then do the registration functions of the class. */
+ print "void SQ" cls "_Register(Squirrel *engine) {"
+ print " DefSQClass <" cls "> SQ" cls "(\"" cls "\");"
+ if (super_cls == "AIObject" || super_cls == "AIAbstractList::Valuator") {
+ print " SQ" cls ".PreRegister(engine);"
+ } else {
+ print " SQ" cls ".PreRegister(engine, \"" super_cls "\");"
+ }
+ if (virtual_class == "false" && super_cls != "AIEvent") {
+ print " SQ" cls ".AddConstructor<void (" cls "::*)(" cls_param[0] "), " cls_param[1]">(engine, \"" cls_param[2] "\");"
+ }
+ print ""
+
+ # Enum values
+ mlen = 0
+ for (i = 1; i <= enum_value_size; i++) {
+ if (mlen <= length(enum_value[i])) mlen = length(enum_value[i])
+ }
+ for (i = 1; i <= enum_value_size; i++) {
+ print " SQ" cls ".DefSQConst(engine, " cls "::" enum_value[i] ", " substr(spaces, 1, mlen - length(enum_value[i])) "\"" enum_value[i] "\");"
+ delete enum_value[i]
+ }
+ if (enum_value_size != 0) print ""
+
+ # Mapping of OTTD strings to errors
+ mlen = 0
+ for (i = 1; i <= enum_string_to_error_size; i++) {
+ if (mlen <= length(enum_string_to_error_mapping_string[i])) mlen = length(enum_string_to_error_mapping_string[i])
+ }
+ for (i = 1; i <= enum_string_to_error_size; i++) {
+ print " AIError::RegisterErrorMap(" enum_string_to_error_mapping_string[i] ", " substr(spaces, 1, mlen - length(enum_string_to_error_mapping_string[i])) cls "::" enum_string_to_error_mapping_error[i] ");"
+
+ delete enum_string_to_error_mapping_string[i]
+ }
+ if (enum_string_to_error_size != 0) print ""
+
+ # Mapping of errors to human 'readable' strings.
+ mlen = 0
+ for (i = 1; i <= enum_error_to_string_size; i++) {
+ if (mlen <= length(enum_error_to_string_mapping[i])) mlen = length(enum_error_to_string_mapping[i])
+ }
+ for (i = 1; i <= enum_error_to_string_size; i++) {
+ print " AIError::RegisterErrorMapString(" cls "::" enum_error_to_string_mapping[i] ", " substr(spaces, 1, mlen - length(enum_error_to_string_mapping[i])) "\"" enum_error_to_string_mapping[i] "\");"
+ delete enum_error_to_string_mapping[i]
+ }
+ if (enum_error_to_string_size != 0) print ""
+
+ # Static methods
+ mlen = 0
+ for (i = 1; i <= static_method_size; i++) {
+ if (mlen <= length(static_methods[i, 0])) mlen = length(static_methods[i, 0])
+ }
+ for (i = 1; i <= static_method_size; i++) {
+ print " SQ" cls ".DefSQStaticMethod(engine, &" cls "::" static_methods[i, 0] ", " substr(spaces, 1, mlen - length(static_methods[i, 0])) "\"" static_methods[i, 0] "\", " substr(spaces, 1, mlen - length(static_methods[i, 0])) "" static_methods[i, 1] ", \"" static_methods[i, 2] "\");"
+ delete static_methods[i]
+ }
+ if (static_method_size != 0) print ""
+
+ if (virtual_class == "false") {
+ # Non-static methods
+ mlen = 0
+ for (i = 1; i <= method_size; i++) {
+ if (mlen <= length(methods[i, 0])) mlen = length(methods[i, 0])
+ }
+ for (i = 1; i <= method_size; i++) {
+ if (methods[i, 2] == "v") {
+ print " SQ" cls ".DefSQAdvancedMethod(engine, &" cls "::" methods[i, 0] ", " substr(spaces, 1, mlen - length(methods[i, 0]) - 8) "\"" methods[i, 0] "\");"
+ } else {
+ print " SQ" cls ".DefSQMethod(engine, &" cls "::" methods[i, 0] ", " substr(spaces, 1, mlen - length(methods[i, 0])) "\"" methods[i, 0] "\", " substr(spaces, 1, mlen - length(methods[i, 0])) "" methods[i, 1] ", \"" methods[i, 2] "\");"
+ }
+ delete methods[i]
+ }
+ if (method_size != 0) print ""
+ }
+ print " SQ" cls ".PostRegister(engine);"
+ print "}"
+
+ enum_size = 0
+ enum_value_size = 0
+ enum_string_to_error_size = 0
+ enum_error_to_string_size = 0
+ struct_size = 0
+ method_size = 0
+ static_method_size = 0
+ virtual_class = "false"
+ cls = ""
+ start_squirrel_define_on_next_line = "false"
+ cls_level = 0
+}
+
+# Skip non-public functions
+{ if (public == "false") next }
+
+# Add enums
+{
+ if (in_enum == "true") {
+ enum_value_size++
+ sub(",", "", $1)
+ enum_value[enum_value_size] = $1
+
+ # Check if this a special error enum
+ if (match(enums[enum_size], ".*::ErrorMessages") != 0) {
+ # syntax:
+ # enum ErrorMessages {
+ # ERR_SOME_ERROR, // [STR_ITEM1, STR_ITEM2, ...]
+ # }
+
+ # Set the mappings
+ if (match($0, "\\[.*\\]") != 0) {
+ mappings = substr($0, RSTART, RLENGTH);
+ gsub("([\\[[:space:]\\]])", "", mappings);
+
+ split(mappings, mapitems, ",");
+ for (i = 1; i <= length(mapitems); i++) {
+ enum_string_to_error_size++
+ enum_string_to_error_mapping_string[enum_string_to_error_size] = mapitems[i]
+ enum_string_to_error_mapping_error[enum_string_to_error_size] = $1
+ }
+
+ enum_error_to_string_size++
+ enum_error_to_string_mapping[enum_error_to_string_size] = $1
+ }
+ }
+ next
+ }
+}
+
+# Add a method to the list
+/^.*\(.*\).*$/ {
+ if (cls_level != 1) next
+ if (match($0, "~")) next
+
+ is_static = match($0, "static")
+ if (match($0, "virtual")) {
+ virtual_class = "true"
+ }
+ gsub("virtual", "", $0)
+ gsub("static", "", $0)
+ gsub("const", "", $0)
+ gsub("{.*", "", $0)
+ param_s = $0
+ gsub("\\*", "", $0)
+ gsub("\\(.*", "", $0)
+
+ sub(".*\\(", "", param_s)
+ sub("\\).*", "", param_s)
+
+ funcname = $2
+ if ($1 == cls && funcname == "") {
+ cls_param[0] = param_s
+ if (param_s == "") next
+ } else if (funcname == "") next
+
+ split(param_s, params, ",")
+ types = "x"
+ for (len = 1; params[len] != ""; len++) {
+ sub("^[ ]*", "", params[len])
+ if (match(params[len], "\\*") || match(params[len], "&")) {
+ if (match(params[len], "^char")) {
+ types = types "s"
+ } else if (match(params[len], "^void")) {
+ types = types "p"
+ } else if (match(params[len], "^Array")) {
+ types = types "a"
+ } else if (match(params[len], "^struct Array")) {
+ types = types "a"
+ } else {
+ types = types "x"
+ }
+ } else if (match(params[len], "^bool")) {
+ types = types "b"
+ } else if (match(params[len], "^HSQUIRRELVM")) {
+ types = "v"
+ } else {
+ types = types "i"
+ }
+ }
+
+ if ($1 == cls && funcname == "") {
+ cls_param[1] = len;
+ cls_param[2] = types;
+ } else if (substr(funcname, 0, 1) == "_" && types != "v") {
+ } else if (is_static) {
+ static_method_size++
+ static_methods[static_method_size, 0] = funcname
+ static_methods[static_method_size, 1] = len
+ static_methods[static_method_size, 2] = types
+ } else {
+ method_size++
+ methods[method_size, 0] = funcname
+ methods[method_size, 1] = len
+ methods[method_size, 2] = types
+ }
+ next
+}
diff --git a/src/ai/api/squirrel_export.sh b/src/ai/api/squirrel_export.sh
new file mode 100644
index 000000000..ac324390f
--- /dev/null
+++ b/src/ai/api/squirrel_export.sh
@@ -0,0 +1,93 @@
+#!/bin/bash
+
+# This must be called from within the src/ai/api directory.
+
+if [ -z "$1" ]; then
+ for f in `ls *.hpp`; do
+ case "${f}" in
+ # these files should not be changed by this script
+ "ai_controller.hpp" | "ai_object.hpp" | "ai_types.hpp" ) continue;
+ esac
+ awk -f squirrel_export.awk ${f} > ${f}.tmp
+ if ! [ -f "${f}.sq" ] || [ -n "`diff -I '$Id' -b ${f}.tmp ${f}.sq 2> /dev/null || echo boo`" ]; then
+ mv ${f}.tmp ${f}.sq
+ echo "Updated: ${f}.sq"
+ svn add ${f}.sq > /dev/null 2>&1
+ svn propset svn:eol-style native ${f}.sq > /dev/null 2>&1
+ svn propset svn:keywords Id ${f}.sq > /dev/null 2>&1
+ else
+ rm -f ${f}.tmp
+ fi
+ done
+else
+ awk -f squirrel_export.awk $1 > $1.tmp
+ if ! [ -f "${f}.sq" ] || [ -n "`diff -I '$Id' -b $1.sq $1.tmp 2> /dev/null || echo boo`" ]; then
+ mv $1.tmp $1.sq
+ echo "Updated: $1.sq"
+ svn add $1.sq > /dev/null 2>&1
+ svn propset svn:eol-style native $1.sq > /dev/null 2>&1
+ svn propset svn:keywords Id $1.sq > /dev/null 2>&1
+ else
+ rm -f $1.tmp
+ fi
+fi
+
+# Remove .hpp.sq if .hpp doesn't exist anymore
+for f in `ls *.hpp.sq`; do
+ f=`echo ${f} | sed "s/.hpp.sq$/.hpp/"`
+ if [ ! -f ${f} ];then
+ echo "Deleted: ${f}.sq"
+ svn del --force ${f}.sq > /dev/null 2>&1
+ fi
+done
+
+# Add stuff to ai_instance.cpp
+f='../ai_instance.cpp'
+
+functions=``
+
+echo "
+{ }
+/.hpp.sq/ { next }
+/squirrel_register_std/ { next }
+/SQAIController_Register/ { print \$0; next }
+/SQAI.*_Register/ { next }
+
+/Note: this line a marker in squirrel_export.sh. Do not change!/ {
+ print \$0
+ gsub(\"^.*/\", \"\")
+ split(\"`grep '^void SQAI.*_Register(Squirrel \*engine) {$' *.hpp.sq | sed 's/:.*$//' | sort | uniq | tr -d '\r' | tr '\n' ' '`\", files, \" \")
+
+ for (i = 1; files[i] != \"\"; i++) {
+ print \"#include \\\"api/\" files[i] \"\\\"\" \$0
+ }
+
+ next;
+}
+
+/\/\* Register all classes \*\// {
+ print \$0
+ gsub(\"^.*/\", \"\")
+ print \" squirrel_register_std(this->engine);\" \$0
+ split(\"`grep '^void SQAI.*_Register(Squirrel \*engine) {$' *.hpp.sq | sed 's/^.*void //;s/Squirrel \*/this->/;s/ {/;/;s/_Register/0000Register/g;' | sort | sed 's/0000Register/_Register/g' | tr -d '\r' | tr '\n' ' '`\", regs, \" \")
+
+ for (i = 1; regs[i] != \"\"; i++) {
+ if (regs[i] == \"SQAIController_Register(this->engine);\") continue
+ print \" \" regs[i] \$0
+ }
+
+ next
+}
+
+{ print \$0; }
+" > ${f}.awk
+
+awk -f ${f}.awk ${f} > ${f}.tmp
+
+if ! [ -f "${f}" ] || [ -n "`diff -I '$Id' -b ${f} ${f}.tmp 2> /dev/null || echo boo`" ]; then
+ mv ${f}.tmp ${f}
+ echo "Updated: ${f}"
+else
+ rm -f ${f}.tmp
+fi
+rm -f ${f}.awk
diff --git a/src/ai/default/default.cpp b/src/ai/default/default.cpp
deleted file mode 100644
index 74d9b7ac5..000000000
--- a/src/ai/default/default.cpp
+++ /dev/null
@@ -1,4028 +0,0 @@
-/* $Id$ */
-
-/** @file default.cpp The original AI. */
-
-#include "../../stdafx.h"
-#include "../../openttd.h"
-#include "../../aircraft.h"
-#include "../../bridge_map.h"
-#include "../../tile_cmd.h"
-#include "../../landscape.h"
-#include "../../rail_map.h"
-#include "../../road_map.h"
-#include "../../roadveh.h"
-#include "../../station_map.h"
-#include "../../tunnel_map.h"
-#include "../../command_func.h"
-#include "../../town.h"
-#include "../../industry.h"
-#include "../../pathfind.h"
-#include "../../airport.h"
-#include "../../variables.h"
-#include "../../bridge.h"
-#include "../../date_func.h"
-#include "../../tunnelbridge_map.h"
-#include "../../window_func.h"
-#include "../../vehicle_func.h"
-#include "../../functions.h"
-#include "../../company_func.h"
-#include "../../company_base.h"
-#include "../../settings_type.h"
-#include "default.h"
-#include "../../tunnelbridge.h"
-#include "../../order_func.h"
-
-#include "../../table/ai_rail.h"
-
-// remove some day perhaps?
-static uint _ai_service_interval;
-CompanyAI _companies_ai[MAX_COMPANIES];
-
-typedef void AiStateAction(Company *c);
-
-enum {
- AIS_0 = 0,
- AIS_1 = 1,
- AIS_VEH_LOOP = 2,
- AIS_VEH_CHECK_REPLACE_VEHICLE = 3,
- AIS_VEH_DO_REPLACE_VEHICLE = 4,
- AIS_WANT_NEW_ROUTE = 5,
- AIS_BUILD_DEFAULT_RAIL_BLOCKS = 6,
- AIS_BUILD_RAIL = 7,
- AIS_BUILD_RAIL_VEH = 8,
- AIS_DELETE_RAIL_BLOCKS = 9,
- AIS_BUILD_DEFAULT_ROAD_BLOCKS = 10,
- AIS_BUILD_ROAD = 11,
- AIS_BUILD_ROAD_VEHICLES = 12,
- AIS_DELETE_ROAD_BLOCKS = 13,
- AIS_AIRPORT_STUFF = 14,
- AIS_BUILD_DEFAULT_AIRPORT_BLOCKS = 15,
- AIS_BUILD_AIRCRAFT_VEHICLES = 16,
- AIS_CHECK_SHIP_STUFF = 17,
- AIS_BUILD_DEFAULT_SHIP_BLOCKS = 18,
- AIS_DO_SHIP_STUFF = 19,
- AIS_SELL_VEHICLE = 20,
- AIS_REMOVE_STATION = 21,
- AIS_REMOVE_TRACK = 22,
- AIS_REMOVE_SINGLE_RAIL_TILE = 23
-};
-
-
-static inline TrackBits GetRailTrackStatus(TileIndex tile)
-{
- return TrackStatusToTrackBits(GetTileTrackStatus(tile, TRANSPORT_RAIL, 0));
-}
-
-
-static void AiCase0(Company *c)
-{
- _companies_ai[c->index].state = AIS_REMOVE_TRACK;
- _companies_ai[c->index].state_counter = 0;
-}
-
-static void AiCase1(Company *c)
-{
- _companies_ai[c->index].cur_veh = NULL;
- _companies_ai[c->index].state = AIS_VEH_LOOP;
-}
-
-static void AiStateVehLoop(Company *c)
-{
- Vehicle *v;
- uint index;
-
- index = (_companies_ai[c->index].cur_veh == NULL) ? 0 : _companies_ai[c->index].cur_veh->index + 1;
-
- FOR_ALL_VEHICLES_FROM(v, index) {
- if (v->owner != _current_company) continue;
-
- if ((v->type == VEH_TRAIN && v->subtype == 0) ||
- v->type == VEH_ROAD ||
- (v->type == VEH_AIRCRAFT && IsNormalAircraft(v)) ||
- v->type == VEH_SHIP) {
- /* replace engine? */
- if (v->type == VEH_TRAIN && v->engine_type < 3 &&
- (_price.build_railvehicle >> 3) < c->money) {
- _companies_ai[c->index].state = AIS_VEH_CHECK_REPLACE_VEHICLE;
- _companies_ai[c->index].cur_veh = v;
- return;
- }
-
- /* not profitable? */
- if (v->age >= 730 &&
- v->profit_last_year < _price.station_value * 5 * 256 &&
- v->profit_this_year < _price.station_value * 5 * 256) {
- _companies_ai[c->index].state_counter = 0;
- _companies_ai[c->index].state = AIS_SELL_VEHICLE;
- _companies_ai[c->index].cur_veh = v;
- return;
- }
-
- /* not reliable? */
- if (v->age >= v->max_age || (
- v->age != 0 &&
- GetEngine(v->engine_type)->reliability < 35389
- )) {
- _companies_ai[c->index].state = AIS_VEH_CHECK_REPLACE_VEHICLE;
- _companies_ai[c->index].cur_veh = v;
- return;
- }
- }
- }
-
- _companies_ai[c->index].state = AIS_WANT_NEW_ROUTE;
- _companies_ai[c->index].state_counter = 0;
-}
-
-static EngineID AiChooseTrainToBuild(RailType railtype, Money money, byte flag, TileIndex tile)
-{
- EngineID best_veh_index = INVALID_ENGINE;
- byte best_veh_score = 0;
- const Engine *e;
-
- FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) {
- EngineID i = e->index;
- const RailVehicleInfo *rvi = &e->u.rail;
-
- if (!IsCompatibleRail(rvi->railtype, railtype) ||
- rvi->railveh_type == RAILVEH_WAGON ||
- (rvi->railveh_type == RAILVEH_MULTIHEAD && flag & 1) ||
- !HasBit(e->company_avail, _current_company) ||
- e->reliability < 0x8A3D) {
- continue;
- }
-
- /* Don't choose an engine designated for passenger use for freight. */
- if (rvi->ai_passenger_only != 0 && flag == 1) continue;
-
- CommandCost ret = DoCommand(tile, i, 0, 0, CMD_BUILD_RAIL_VEHICLE);
- if (CmdSucceeded(ret) && ret.GetCost() <= money && rvi->ai_rank >= best_veh_score) {
- best_veh_score = rvi->ai_rank;
- best_veh_index = i;
- }
- }
-
- return best_veh_index;
-}
-
-static EngineID AiChooseRoadVehToBuild(CargoID cargo, Money money, TileIndex tile)
-{
- EngineID best_veh_index = INVALID_ENGINE;
- int32 best_veh_rating = 0;
- const Engine *e;
-
- FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) {
- EngineID i = e->index;
- const RoadVehicleInfo *rvi = &e->u.road;
-
- if (!HasBit(e->company_avail, _current_company) || e->reliability < 0x8A3D) {
- continue;
- }
-
- /* Skip vehicles which can't take our cargo type */
- if (rvi->cargo_type != cargo && !CanRefitTo(i, cargo)) continue;
-
- /* Rate and compare the engine by speed & capacity */
- int rating = rvi->max_speed * rvi->capacity;
- if (rating <= best_veh_rating) continue;
-
- CommandCost ret = DoCommand(tile, i, 0, 0, CMD_BUILD_ROAD_VEH);
- if (CmdFailed(ret)) continue;
-
- /* Add the cost of refitting */
- if (rvi->cargo_type != cargo) ret.AddCost(GetRefitCost(i));
- if (ret.GetCost() > money) continue;
-
- best_veh_rating = rating;
- best_veh_index = i;
- }
-
- return best_veh_index;
-}
-
-/**
- * Choose aircraft to build.
- * @param money current AI money
- * @param forbidden forbidden flags - AIR_HELI = 0 (always allowed), AIR_CTOL = 1 (bit 0), AIR_FAST = 2 (bit 1)
- * @return EngineID of aircraft to build
- */
-static EngineID AiChooseAircraftToBuild(Money money, byte forbidden)
-{
- EngineID best_veh_index = INVALID_ENGINE;
- Money best_veh_cost = 0;
- const Engine *e;
-
- FOR_ALL_ENGINES_OF_TYPE(e, VEH_AIRCRAFT) {
- EngineID i = e->index;
- const AircraftVehicleInfo *avi = &e->u.air;
-
- if (!HasBit(e->company_avail, _current_company) || e->reliability < 0x8A3D) {
- continue;
- }
-
- if ((avi->subtype & forbidden) != 0) continue;
-
- CommandCost ret = DoCommand(0, i, 0, DC_QUERY_COST, CMD_BUILD_AIRCRAFT);
- if (CmdSucceeded(ret) && ret.GetCost() <= money && ret.GetCost() >= best_veh_cost) {
- best_veh_cost = ret.GetCost();
- best_veh_index = i;
- }
- }
-
- return best_veh_index;
-}
-
-static Money AiGetBasePrice(const Company *c)
-{
- Money base = _price.station_value;
-
- // adjust base price when more expensive vehicles are available
- switch (_companies_ai[c->index].railtype_to_use) {
- default: NOT_REACHED();
- case RAILTYPE_RAIL: break;
- case RAILTYPE_ELECTRIC: break;
- case RAILTYPE_MONO: base = (base * 3) >> 1; break;
- case RAILTYPE_MAGLEV: base *= 2; break;
- }
-
- return base;
-}
-
-static EngineID AiChooseRoadVehToReplaceWith(const Company *c, const Vehicle *v)
-{
- Money avail_money = c->money + v->value;
- return AiChooseRoadVehToBuild(v->cargo_type, avail_money, v->tile);
-}
-
-static EngineID AiChooseAircraftToReplaceWith(const Company *c, const Vehicle *v)
-{
- Money avail_money = c->money + v->value;
-
- /* determine forbidden aircraft bits */
- byte forbidden = 0;
- const Order *o;
-
- FOR_VEHICLE_ORDERS(v, o) {
- if (!o->IsValid()) continue;
- if (!IsValidStationID(o->GetDestination())) continue;
- const Station *st = GetStation(o->GetDestination());
- if (!(st->facilities & FACIL_AIRPORT)) continue;
-
- AirportFTAClass::Flags flags = st->Airport()->flags;
- if (!(flags & AirportFTAClass::AIRPLANES)) forbidden |= AIR_CTOL | AIR_FAST; // no planes for heliports / oil rigs
- if (flags & AirportFTAClass::SHORT_STRIP) forbidden |= AIR_FAST; // no fast planes for small airports
- }
-
- return AiChooseAircraftToBuild(
- avail_money, forbidden
- );
-}
-
-static EngineID AiChooseTrainToReplaceWith(const Company *c, const Vehicle *v)
-{
- Money avail_money = c->money + v->value;
- const Vehicle *u = v;
- int num = 0;
-
- while (++num, u->Next() != NULL) {
- u = u->Next();
- }
-
- // XXX: check if a wagon
- return AiChooseTrainToBuild(v->u.rail.railtype, avail_money, 0, v->tile);
-}
-
-static EngineID AiChooseShipToReplaceWith(const Company *c, const Vehicle *v)
-{
- /* Ships are not implemented in this (broken) AI */
- return INVALID_ENGINE;
-}
-
-static void AiHandleGotoDepot(Company *c, int cmd)
-{
- if (!_companies_ai[c->index].cur_veh->current_order.IsType(OT_GOTO_DEPOT))
- DoCommand(0, _companies_ai[c->index].cur_veh->index, 0, DC_EXEC, cmd);
-
- if (++_companies_ai[c->index].state_counter <= 1387) {
- _companies_ai[c->index].state = AIS_VEH_DO_REPLACE_VEHICLE;
- return;
- }
-
- if (_companies_ai[c->index].cur_veh->current_order.IsType(OT_GOTO_DEPOT)) {
- _companies_ai[c->index].cur_veh->current_order.MakeDummy();
- InvalidateWindow(WC_VEHICLE_VIEW, _companies_ai[c->index].cur_veh->index);
- }
-}
-
-static void AiRestoreVehicleOrders(Vehicle *v, BackuppedOrders *bak)
-{
- if (bak->order == NULL) return;
-
- for (uint i = 0; !bak->order[i].IsType(OT_NOTHING); i++) {
- if (!DoCommandP(0, v->index + (i << 16), bak->order[i].Pack(), CMD_INSERT_ORDER | CMD_NO_TEST_IF_IN_NETWORK))
- break;
- }
-}
-
-static void AiHandleReplaceTrain(Company *c)
-{
- const Vehicle *v = _companies_ai[c->index].cur_veh;
- BackuppedOrders orderbak;
- EngineID veh;
-
- // wait until the vehicle reaches the depot.
- if (!IsRailDepotTile(v->tile) || v->u.rail.track != TRACK_BIT_DEPOT || !(v->vehstatus & VS_STOPPED)) {
- AiHandleGotoDepot(c, CMD_SEND_TRAIN_TO_DEPOT);
- return;
- }
-
- veh = AiChooseTrainToReplaceWith(c, v);
- if (veh != INVALID_ENGINE) {
- TileIndex tile;
-
- BackupVehicleOrders(v, &orderbak);
- tile = v->tile;
-
- if (CmdSucceeded(DoCommand(0, v->index, 2, DC_EXEC, CMD_SELL_RAIL_WAGON)) &&
- CmdSucceeded(DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_RAIL_VEHICLE))) {
- VehicleID veh = _new_vehicle_id;
- AiRestoreVehicleOrders(GetVehicle(veh), &orderbak);
- DoCommand(0, veh, 0, DC_EXEC, CMD_START_STOP_VEHICLE);
-
- DoCommand(0, veh, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT);
- }
- }
-}
-
-static void AiHandleReplaceRoadVeh(Company *c)
-{
- const Vehicle *v = _companies_ai[c->index].cur_veh;
- BackuppedOrders orderbak;
- EngineID veh;
-
- if (!v->IsStoppedInDepot()) {
- AiHandleGotoDepot(c, CMD_SEND_ROADVEH_TO_DEPOT);
- return;
- }
-
- veh = AiChooseRoadVehToReplaceWith(c, v);
- if (veh != INVALID_ENGINE) {
- TileIndex tile;
-
- BackupVehicleOrders(v, &orderbak);
- tile = v->tile;
-
- if (CmdSucceeded(DoCommand(0, v->index, 0, DC_EXEC, CMD_SELL_ROAD_VEH)) &&
- CmdSucceeded(DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_ROAD_VEH))) {
- VehicleID veh = _new_vehicle_id;
-
- AiRestoreVehicleOrders(GetVehicle(veh), &orderbak);
- DoCommand(0, veh, 0, DC_EXEC, CMD_START_STOP_VEHICLE);
- DoCommand(0, veh, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT);
- }
- }
-}
-
-static void AiHandleReplaceAircraft(Company *c)
-{
- const Vehicle *v = _companies_ai[c->index].cur_veh;
- BackuppedOrders orderbak;
- EngineID veh;
-
- if (!v->IsStoppedInDepot()) {
- AiHandleGotoDepot(c, CMD_SEND_AIRCRAFT_TO_HANGAR);
- return;
- }
-
- veh = AiChooseAircraftToReplaceWith(c, v);
- if (veh != INVALID_ENGINE) {
- TileIndex tile;
-
- BackupVehicleOrders(v, &orderbak);
- tile = v->tile;
-
- if (CmdSucceeded(DoCommand(0, v->index, 0, DC_EXEC, CMD_SELL_AIRCRAFT)) &&
- CmdSucceeded(DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_AIRCRAFT))) {
- VehicleID veh = _new_vehicle_id;
- AiRestoreVehicleOrders(GetVehicle(veh), &orderbak);
- DoCommand(0, veh, 0, DC_EXEC, CMD_START_STOP_VEHICLE);
-
- DoCommand(0, veh, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT);
- }
- }
-}
-
-static void AiHandleReplaceShip(Company *c)
-{
- /* Ships are not implemented in this (broken) AI */
-}
-
-typedef EngineID CheckReplaceProc(const Company *c, const Vehicle *v);
-
-static CheckReplaceProc * const _veh_check_replace_proc[] = {
- AiChooseTrainToReplaceWith,
- AiChooseRoadVehToReplaceWith,
- AiChooseShipToReplaceWith,
- AiChooseAircraftToReplaceWith,
-};
-
-typedef void DoReplaceProc(Company *c);
-static DoReplaceProc * const _veh_do_replace_proc[] = {
- AiHandleReplaceTrain,
- AiHandleReplaceRoadVeh,
- AiHandleReplaceShip,
- AiHandleReplaceAircraft
-};
-
-static void AiStateCheckReplaceVehicle(Company *c)
-{
- const Vehicle *v = _companies_ai[c->index].cur_veh;
-
- if (!v->IsValid() ||
- v->owner != _current_company ||
- v->type > VEH_SHIP ||
- _veh_check_replace_proc[v->type - VEH_TRAIN](c, v) == INVALID_ENGINE) {
- _companies_ai[c->index].state = AIS_VEH_LOOP;
- } else {
- _companies_ai[c->index].state_counter = 0;
- _companies_ai[c->index].state = AIS_VEH_DO_REPLACE_VEHICLE;
- }
-}
-
-static void AiStateDoReplaceVehicle(Company *c)
-{
- const Vehicle *v = _companies_ai[c->index].cur_veh;
-
- _companies_ai[c->index].state = AIS_VEH_LOOP;
- // vehicle is not owned by the company anymore, something went very wrong.
- if (!v->IsValid() || v->owner != _current_company) return;
- _veh_do_replace_proc[v->type - VEH_TRAIN](c);
-}
-
-struct FoundRoute {
- int distance;
- CargoID cargo;
- void *from;
- void *to;
-};
-
-static Town *AiFindRandomTown()
-{
- return GetRandomTown();
-}
-
-static Industry *AiFindRandomIndustry()
-{
- int num = RandomRange(GetMaxIndustryIndex());
- if (IsValidIndustryID(num)) return GetIndustry(num);
-
- return NULL;
-}
-
-static void AiFindSubsidyIndustryRoute(FoundRoute *fr)
-{
- uint i;
- CargoID cargo;
- const Subsidy *s;
- Industry *from;
- TileIndex to_xy;
-
- // initially error
- fr->distance = -1;
-
- // Randomize subsidy index..
- i = RandomRange(lengthof(_subsidies) * 3);
- if (i >= lengthof(_subsidies)) return;
-
- s = &_subsidies[i];
-
- // Don't want passengers or mail
- cargo = s->cargo_type;
- if (cargo == CT_INVALID ||
- cargo == CT_PASSENGERS ||
- cargo == CT_MAIL ||
- s->age > 7) {
- return;
- }
- fr->cargo = cargo;
-
- fr->from = from = GetIndustry(s->from);
-
- if (cargo == CT_GOODS || cargo == CT_FOOD) {
- Town *to_tow = GetTown(s->to);
-
- if (to_tow->population < (cargo == CT_FOOD ? 200U : 900U)) return; // error
- fr->to = to_tow;
- to_xy = to_tow->xy;
- } else {
- Industry *to_ind = GetIndustry(s->to);
-
- fr->to = to_ind;
- to_xy = to_ind->xy;
- }
-
- fr->distance = DistanceManhattan(from->xy, to_xy);
-}
-
-static void AiFindSubsidyPassengerRoute(FoundRoute *fr)
-{
- uint i;
- const Subsidy *s;
- Town *from, *to;
-
- // initially error
- fr->distance = -1;
-
- // Randomize subsidy index..
- i = RandomRange(lengthof(_subsidies) * 3);
- if (i >= lengthof(_subsidies)) return;
-
- s = &_subsidies[i];
-
- // Only want passengers
- if (s->cargo_type != CT_PASSENGERS || s->age > 7) return;
- fr->cargo = s->cargo_type;
-
- fr->from = from = GetTown(s->from);
- fr->to = to = GetTown(s->to);
-
- // They must be big enough
- if (from->population < 400 || to->population < 400) return;
-
- fr->distance = DistanceManhattan(from->xy, to->xy);
-}
-
-static void AiFindRandomIndustryRoute(FoundRoute *fr)
-{
- Industry *i;
- uint32 r;
- CargoID cargo;
-
- // initially error
- fr->distance = -1;
-
- r = Random();
-
- // pick a source
- fr->from = i = AiFindRandomIndustry();
- if (i == NULL) return;
-
- // pick a random produced cargo
- cargo = i->produced_cargo[0];
- if (r & 1 && i->produced_cargo[1] != CT_INVALID) cargo = i->produced_cargo[1];
-
- fr->cargo = cargo;
-
- // don't allow passengers
- if (cargo == CT_INVALID || cargo == CT_PASSENGERS) return;
-
- if (cargo != CT_GOODS && cargo != CT_FOOD) {
- // pick a dest, and see if it can receive
- Industry *i2 = AiFindRandomIndustry();
- if (i2 == NULL || i == i2 ||
- (i2->accepts_cargo[0] != cargo &&
- i2->accepts_cargo[1] != cargo &&
- i2->accepts_cargo[2] != cargo)) {
- return;
- }
-
- fr->to = i2;
- fr->distance = DistanceManhattan(i->xy, i2->xy);
- } else {
- // pick a dest town, and see if it's big enough
- Town *t = AiFindRandomTown();
-
- if (t == NULL || t->population < (cargo == CT_FOOD ? 200U : 900U)) return;
-
- fr->to = t;
- fr->distance = DistanceManhattan(i->xy, t->xy);
- }
-}
-
-static void AiFindRandomPassengerRoute(FoundRoute *fr)
-{
- Town *source;
- Town *dest;
-
- // initially error
- fr->distance = -1;
-
- fr->from = source = AiFindRandomTown();
- if (source == NULL || source->population < 400) return;
-
- fr->to = dest = AiFindRandomTown();
- if (dest == NULL || source == dest || dest->population < 400) return;
-
- fr->distance = DistanceManhattan(source->xy, dest->xy);
-}
-
-// Warn: depends on 'xy' being the first element in both Town and Industry
-#define GET_TOWN_OR_INDUSTRY_TILE(p) (((Town*)(p))->xy)
-
-static bool AiCheckIfRouteIsGood(Company *c, FoundRoute *fr, byte bitmask)
-{
- TileIndex from_tile, to_tile;
- Station *st;
- int dist;
- uint same_station = 0;
-
- from_tile = GET_TOWN_OR_INDUSTRY_TILE(fr->from);
- to_tile = GET_TOWN_OR_INDUSTRY_TILE(fr->to);
-
- dist = 0xFFFF;
- FOR_ALL_STATIONS(st) {
- int cur;
-
- if (st->owner != _current_company) continue;
- cur = DistanceMax(from_tile, st->xy);
- if (cur < dist) dist = cur;
- cur = DistanceMax(to_tile, st->xy);
- if (cur < dist) dist = cur;
- if (to_tile == from_tile && st->xy == to_tile) same_station++;
- }
-
- // To prevent the AI from building ten busstations in the same town, do some calculations
- // For each road or airport station, we want 350 of population!
- if ((bitmask == 2 || bitmask == 4) &&
- same_station > 2 &&
- ((Town*)fr->from)->population < same_station * 350) {
- return false;
- }
-
- /* Requiring distance to nearest station to be always under 37 tiles may be suboptimal,
- * Especially for longer aircraft routes that start and end pretty at any arbitrary place on map
- * While it may be nice for AI to cluster their creations together, hardcoded limit is not ideal.
- * If AI will randomly start on some isolated spot, it will never get out of there.
- * AI will have chance of randomly rejecting routes further than 37 tiles from their network,
- * so there will be some attempt to cluster the network together */
-
- /* Random value between 37 and 292. Low values are exponentially more likely
- * With 50% chance the value will be under 52 tiles */
- int min_distance = 36 + (1 << (Random() % 9)); // 0..8
-
- /* Make sure distance to closest station is < min_distance tiles. */
- if (dist != 0xFFFF && dist > min_distance) return false;
-
- if (_companies_ai[c->index].route_type_mask != 0 &&
- !(_companies_ai[c->index].route_type_mask & bitmask) &&
- !Chance16(1, 5)) {
- return false;
- }
-
- if (fr->cargo == CT_PASSENGERS || fr->cargo == CT_MAIL) {
- const Town *from = (const Town*)fr->from;
- const Town *to = (const Town*)fr->to;
-
- if (from->pct_pass_transported > 0x99 ||
- to->pct_pass_transported > 0x99) {
- return false;
- }
-
- // Make sure it has a reasonably good rating
- if (from->ratings[_current_company] < -100 ||
- to->ratings[_current_company] < -100) {
- return false;
- }
- } else {
- const Industry *i = (const Industry*)fr->from;
-
- if (i->last_month_pct_transported[fr->cargo != i->produced_cargo[0]] > 0x99 ||
- i->last_month_production[fr->cargo != i->produced_cargo[0]] == 0) {
- return false;
- }
- }
-
- _companies_ai[c->index].route_type_mask |= bitmask;
- return true;
-}
-
-static byte AiGetDirectionBetweenTiles(TileIndex a, TileIndex b)
-{
- byte i = (TileX(a) < TileX(b)) ? 1 : 0;
- if (TileY(a) >= TileY(b)) i ^= 3;
- return i;
-}
-
-static TileIndex AiGetPctTileBetween(TileIndex a, TileIndex b, byte pct)
-{
- return TileXY(
- TileX(a) + ((TileX(b) - TileX(a)) * pct >> 8),
- TileY(a) + ((TileY(b) - TileY(a)) * pct >> 8)
- );
-}
-
-static void AiWantLongIndustryRoute(Company *c)
-{
- int i;
- FoundRoute fr;
-
- i = 60;
- for (;;) {
- // look for one from the subsidy list
- AiFindSubsidyIndustryRoute(&fr);
- if (IsInsideMM(fr.distance, 60, 90 + 1)) break;
-
- // try a random one
- AiFindRandomIndustryRoute(&fr);
- if (IsInsideMM(fr.distance, 60, 90 + 1)) break;
-
- // only test 60 times
- if (--i == 0) return;
- }
-
- if (!AiCheckIfRouteIsGood(c, &fr, 1)) return;
-
- // Fill the source field
- _companies_ai[c->index].dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to);
- _companies_ai[c->index].src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from);
-
- _companies_ai[c->index].src.use_tile = 0;
- _companies_ai[c->index].src.rand_rng = 9;
- _companies_ai[c->index].src.cur_building_rule = 0xFF;
- _companies_ai[c->index].src.unk6 = 1;
- _companies_ai[c->index].src.unk7 = 0;
- _companies_ai[c->index].src.buildcmd_a = 0x24;
- _companies_ai[c->index].src.buildcmd_b = 0xFF;
- _companies_ai[c->index].src.direction = AiGetDirectionBetweenTiles(
- _companies_ai[c->index].src.spec_tile,
- _companies_ai[c->index].dst.spec_tile
- );
- _companies_ai[c->index].src.cargo = fr.cargo | 0x80;
-
- // Fill the dest field
-
- _companies_ai[c->index].dst.use_tile = 0;
- _companies_ai[c->index].dst.rand_rng = 9;
- _companies_ai[c->index].dst.cur_building_rule = 0xFF;
- _companies_ai[c->index].dst.unk6 = 1;
- _companies_ai[c->index].dst.unk7 = 0;
- _companies_ai[c->index].dst.buildcmd_a = 0x34;
- _companies_ai[c->index].dst.buildcmd_b = 0xFF;
- _companies_ai[c->index].dst.direction = AiGetDirectionBetweenTiles(
- _companies_ai[c->index].dst.spec_tile,
- _companies_ai[c->index].src.spec_tile
- );
- _companies_ai[c->index].dst.cargo = fr.cargo;
-
- // Fill middle field 1
- _companies_ai[c->index].mid1.spec_tile = AiGetPctTileBetween(
- _companies_ai[c->index].src.spec_tile,
- _companies_ai[c->index].dst.spec_tile,
- 0x55
- );
- _companies_ai[c->index].mid1.use_tile = 0;
- _companies_ai[c->index].mid1.rand_rng = 6;
- _companies_ai[c->index].mid1.cur_building_rule = 0xFF;
- _companies_ai[c->index].mid1.unk6 = 2;
- _companies_ai[c->index].mid1.unk7 = 1;
- _companies_ai[c->index].mid1.buildcmd_a = 0x30;
- _companies_ai[c->index].mid1.buildcmd_b = 0xFF;
- _companies_ai[c->index].mid1.direction = _companies_ai[c->index].src.direction;
- _companies_ai[c->index].mid1.cargo = fr.cargo;
-
- // Fill middle field 2
- _companies_ai[c->index].mid2.spec_tile = AiGetPctTileBetween(
- _companies_ai[c->index].src.spec_tile,
- _companies_ai[c->index].dst.spec_tile,
- 0xAA
- );
- _companies_ai[c->index].mid2.use_tile = 0;
- _companies_ai[c->index].mid2.rand_rng = 6;
- _companies_ai[c->index].mid2.cur_building_rule = 0xFF;
- _companies_ai[c->index].mid2.unk6 = 2;
- _companies_ai[c->index].mid2.unk7 = 1;
- _companies_ai[c->index].mid2.buildcmd_a = 0xFF;
- _companies_ai[c->index].mid2.buildcmd_b = 0xFF;
- _companies_ai[c->index].mid2.direction = _companies_ai[c->index].dst.direction;
- _companies_ai[c->index].mid2.cargo = fr.cargo;
-
- // Fill common fields
- _companies_ai[c->index].cargo_type = fr.cargo;
- _companies_ai[c->index].num_wagons = 3;
- _companies_ai[c->index].build_kind = 2;
- _companies_ai[c->index].num_build_rec = 4;
- _companies_ai[c->index].num_loco_to_build = 2;
- _companies_ai[c->index].num_want_fullload = 2;
- _companies_ai[c->index].wagon_list[0] = INVALID_VEHICLE;
- _companies_ai[c->index].order_list_blocks[0] = 0;
- _companies_ai[c->index].order_list_blocks[1] = 1;
- _companies_ai[c->index].order_list_blocks[2] = 255;
-
- _companies_ai[c->index].state = AIS_BUILD_DEFAULT_RAIL_BLOCKS;
- _companies_ai[c->index].state_mode = UCHAR_MAX;
- _companies_ai[c->index].state_counter = 0;
- _companies_ai[c->index].timeout_counter = 0;
-}
-
-static void AiWantMediumIndustryRoute(Company *c)
-{
- int i;
- FoundRoute fr;
-
- i = 60;
- for (;;) {
- // look for one from the subsidy list
- AiFindSubsidyIndustryRoute(&fr);
- if (IsInsideMM(fr.distance, 40, 60 + 1)) break;
-
- // try a random one
- AiFindRandomIndustryRoute(&fr);
- if (IsInsideMM(fr.distance, 40, 60 + 1)) break;
-
- // only test 60 times
- if (--i == 0) return;
- }
-
- if (!AiCheckIfRouteIsGood(c, &fr, 1)) return;
-
- // Fill the source field
- _companies_ai[c->index].src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from);
- _companies_ai[c->index].src.use_tile = 0;
- _companies_ai[c->index].src.rand_rng = 9;
- _companies_ai[c->index].src.cur_building_rule = 0xFF;
- _companies_ai[c->index].src.unk6 = 1;
- _companies_ai[c->index].src.unk7 = 0;
- _companies_ai[c->index].src.buildcmd_a = 0x10;
- _companies_ai[c->index].src.buildcmd_b = 0xFF;
- _companies_ai[c->index].src.direction = AiGetDirectionBetweenTiles(
- GET_TOWN_OR_INDUSTRY_TILE(fr.from),
- GET_TOWN_OR_INDUSTRY_TILE(fr.to)
- );
- _companies_ai[c->index].src.cargo = fr.cargo | 0x80;
-
- // Fill the dest field
- _companies_ai[c->index].dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to);
- _companies_ai[c->index].dst.use_tile = 0;
- _companies_ai[c->index].dst.rand_rng = 9;
- _companies_ai[c->index].dst.cur_building_rule = 0xFF;
- _companies_ai[c->index].dst.unk6 = 1;
- _companies_ai[c->index].dst.unk7 = 0;
- _companies_ai[c->index].dst.buildcmd_a = 0xFF;
- _companies_ai[c->index].dst.buildcmd_b = 0xFF;
- _companies_ai[c->index].dst.direction = AiGetDirectionBetweenTiles(
- GET_TOWN_OR_INDUSTRY_TILE(fr.to),
- GET_TOWN_OR_INDUSTRY_TILE(fr.from)
- );
- _companies_ai[c->index].dst.cargo = fr.cargo;
-
- // Fill common fields
- _companies_ai[c->index].cargo_type = fr.cargo;
- _companies_ai[c->index].num_wagons = 3;
- _companies_ai[c->index].build_kind = 1;
- _companies_ai[c->index].num_build_rec = 2;
- _companies_ai[c->index].num_loco_to_build = 1;
- _companies_ai[c->index].num_want_fullload = 1;
- _companies_ai[c->index].wagon_list[0] = INVALID_VEHICLE;
- _companies_ai[c->index].order_list_blocks[0] = 0;
- _companies_ai[c->index].order_list_blocks[1] = 1;
- _companies_ai[c->index].order_list_blocks[2] = 255;
- _companies_ai[c->index].state = AIS_BUILD_DEFAULT_RAIL_BLOCKS;
- _companies_ai[c->index].state_mode = UCHAR_MAX;
- _companies_ai[c->index].state_counter = 0;
- _companies_ai[c->index].timeout_counter = 0;
-}
-
-static void AiWantShortIndustryRoute(Company *c)
-{
- int i;
- FoundRoute fr;
-
- i = 60;
- for (;;) {
- // look for one from the subsidy list
- AiFindSubsidyIndustryRoute(&fr);
- if (IsInsideMM(fr.distance, 15, 40 + 1)) break;
-
- // try a random one
- AiFindRandomIndustryRoute(&fr);
- if (IsInsideMM(fr.distance, 15, 40 + 1)) break;
-
- // only test 60 times
- if (--i == 0) return;
- }
-
- if (!AiCheckIfRouteIsGood(c, &fr, 1)) return;
-
- // Fill the source field
- _companies_ai[c->index].src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from);
- _companies_ai[c->index].src.use_tile = 0;
- _companies_ai[c->index].src.rand_rng = 9;
- _companies_ai[c->index].src.cur_building_rule = 0xFF;
- _companies_ai[c->index].src.unk6 = 1;
- _companies_ai[c->index].src.unk7 = 0;
- _companies_ai[c->index].src.buildcmd_a = 0x10;
- _companies_ai[c->index].src.buildcmd_b = 0xFF;
- _companies_ai[c->index].src.direction = AiGetDirectionBetweenTiles(
- GET_TOWN_OR_INDUSTRY_TILE(fr.from),
- GET_TOWN_OR_INDUSTRY_TILE(fr.to)
- );
- _companies_ai[c->index].src.cargo = fr.cargo | 0x80;
-
- // Fill the dest field
- _companies_ai[c->index].dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to);
- _companies_ai[c->index].dst.use_tile = 0;
- _companies_ai[c->index].dst.rand_rng = 9;
- _companies_ai[c->index].dst.cur_building_rule = 0xFF;
- _companies_ai[c->index].dst.unk6 = 1;
- _companies_ai[c->index].dst.unk7 = 0;
- _companies_ai[c->index].dst.buildcmd_a = 0xFF;
- _companies_ai[c->index].dst.buildcmd_b = 0xFF;
- _companies_ai[c->index].dst.direction = AiGetDirectionBetweenTiles(
- GET_TOWN_OR_INDUSTRY_TILE(fr.to),
- GET_TOWN_OR_INDUSTRY_TILE(fr.from)
- );
- _companies_ai[c->index].dst.cargo = fr.cargo;
-
- // Fill common fields
- _companies_ai[c->index].cargo_type = fr.cargo;
- _companies_ai[c->index].num_wagons = 2;
- _companies_ai[c->index].build_kind = 1;
- _companies_ai[c->index].num_build_rec = 2;
- _companies_ai[c->index].num_loco_to_build = 1;
- _companies_ai[c->index].num_want_fullload = 1;
- _companies_ai[c->index].wagon_list[0] = INVALID_VEHICLE;
- _companies_ai[c->index].order_list_blocks[0] = 0;
- _companies_ai[c->index].order_list_blocks[1] = 1;
- _companies_ai[c->index].order_list_blocks[2] = 255;
- _companies_ai[c->index].state = AIS_BUILD_DEFAULT_RAIL_BLOCKS;
- _companies_ai[c->index].state_mode = UCHAR_MAX;
- _companies_ai[c->index].state_counter = 0;
- _companies_ai[c->index].timeout_counter = 0;
-}
-
-static void AiWantMailRoute(Company *c)
-{
- int i;
- FoundRoute fr;
-
- i = 60;
- for (;;) {
- // look for one from the subsidy list
- AiFindSubsidyPassengerRoute(&fr);
- if (IsInsideMM(fr.distance, 60, 110 + 1)) break;
-
- // try a random one
- AiFindRandomPassengerRoute(&fr);
- if (IsInsideMM(fr.distance, 60, 110 + 1)) break;
-
- // only test 60 times
- if (--i == 0) return;
- }
-
- fr.cargo = CT_MAIL;
- if (!AiCheckIfRouteIsGood(c, &fr, 1)) return;
-
- // Fill the source field
- _companies_ai[c->index].src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from);
- _companies_ai[c->index].src.use_tile = 0;
- _companies_ai[c->index].src.rand_rng = 7;
- _companies_ai[c->index].src.cur_building_rule = 0xFF;
- _companies_ai[c->index].src.unk6 = 1;
- _companies_ai[c->index].src.unk7 = 0;
- _companies_ai[c->index].src.buildcmd_a = 0x24;
- _companies_ai[c->index].src.buildcmd_b = 0xFF;
- _companies_ai[c->index].src.direction = AiGetDirectionBetweenTiles(
- GET_TOWN_OR_INDUSTRY_TILE(fr.from),
- GET_TOWN_OR_INDUSTRY_TILE(fr.to)
- );
- _companies_ai[c->index].src.cargo = fr.cargo;
-
- // Fill the dest field
- _companies_ai[c->index].dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to);
- _companies_ai[c->index].dst.use_tile = 0;
- _companies_ai[c->index].dst.rand_rng = 7;
- _companies_ai[c->index].dst.cur_building_rule = 0xFF;
- _companies_ai[c->index].dst.unk6 = 1;
- _companies_ai[c->index].dst.unk7 = 0;
- _companies_ai[c->index].dst.buildcmd_a = 0x34;
- _companies_ai[c->index].dst.buildcmd_b = 0xFF;
- _companies_ai[c->index].dst.direction = AiGetDirectionBetweenTiles(
- GET_TOWN_OR_INDUSTRY_TILE(fr.to),
- GET_TOWN_OR_INDUSTRY_TILE(fr.from)
- );
- _companies_ai[c->index].dst.cargo = fr.cargo;
-
- // Fill middle field 1
- _companies_ai[c->index].mid1.spec_tile = AiGetPctTileBetween(
- GET_TOWN_OR_INDUSTRY_TILE(fr.from),
- GET_TOWN_OR_INDUSTRY_TILE(fr.to),
- 0x55
- );
- _companies_ai[c->index].mid1.use_tile = 0;
- _companies_ai[c->index].mid1.rand_rng = 6;
- _companies_ai[c->index].mid1.cur_building_rule = 0xFF;
- _companies_ai[c->index].mid1.unk6 = 2;
- _companies_ai[c->index].mid1.unk7 = 1;
- _companies_ai[c->index].mid1.buildcmd_a = 0x30;
- _companies_ai[c->index].mid1.buildcmd_b = 0xFF;
- _companies_ai[c->index].mid1.direction = _companies_ai[c->index].src.direction;
- _companies_ai[c->index].mid1.cargo = fr.cargo;
-
- // Fill middle field 2
- _companies_ai[c->index].mid2.spec_tile = AiGetPctTileBetween(
- GET_TOWN_OR_INDUSTRY_TILE(fr.from),
- GET_TOWN_OR_INDUSTRY_TILE(fr.to),
- 0xAA
- );
- _companies_ai[c->index].mid2.use_tile = 0;
- _companies_ai[c->index].mid2.rand_rng = 6;
- _companies_ai[c->index].mid2.cur_building_rule = 0xFF;
- _companies_ai[c->index].mid2.unk6 = 2;
- _companies_ai[c->index].mid2.unk7 = 1;
- _companies_ai[c->index].mid2.buildcmd_a = 0xFF;
- _companies_ai[c->index].mid2.buildcmd_b = 0xFF;
- _companies_ai[c->index].mid2.direction = _companies_ai[c->index].dst.direction;
- _companies_ai[c->index].mid2.cargo = fr.cargo;
-
- // Fill common fields
- _companies_ai[c->index].cargo_type = fr.cargo;
- _companies_ai[c->index].num_wagons = 3;
- _companies_ai[c->index].build_kind = 2;
- _companies_ai[c->index].num_build_rec = 4;
- _companies_ai[c->index].num_loco_to_build = 2;
- _companies_ai[c->index].num_want_fullload = 0;
- _companies_ai[c->index].wagon_list[0] = INVALID_VEHICLE;
- _companies_ai[c->index].order_list_blocks[0] = 0;
- _companies_ai[c->index].order_list_blocks[1] = 1;
- _companies_ai[c->index].order_list_blocks[2] = 255;
- _companies_ai[c->index].state = AIS_BUILD_DEFAULT_RAIL_BLOCKS;
- _companies_ai[c->index].state_mode = UCHAR_MAX;
- _companies_ai[c->index].state_counter = 0;
- _companies_ai[c->index].timeout_counter = 0;
-}
-
-static void AiWantPassengerRoute(Company *c)
-{
- int i;
- FoundRoute fr;
-
- i = 60;
- for (;;) {
- // look for one from the subsidy list
- AiFindSubsidyPassengerRoute(&fr);
- if (IsInsideMM(fr.distance, 0, 55 + 1)) break;
-
- // try a random one
- AiFindRandomPassengerRoute(&fr);
- if (IsInsideMM(fr.distance, 0, 55 + 1)) break;
-
- // only test 60 times
- if (--i == 0) return;
- }
-
- fr.cargo = CT_PASSENGERS;
- if (!AiCheckIfRouteIsGood(c, &fr, 1)) return;
-
- // Fill the source field
- _companies_ai[c->index].src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from);
- _companies_ai[c->index].src.use_tile = 0;
- _companies_ai[c->index].src.rand_rng = 7;
- _companies_ai[c->index].src.cur_building_rule = 0xFF;
- _companies_ai[c->index].src.unk6 = 1;
- _companies_ai[c->index].src.unk7 = 0;
- _companies_ai[c->index].src.buildcmd_a = 0x10;
- _companies_ai[c->index].src.buildcmd_b = 0xFF;
- _companies_ai[c->index].src.direction = AiGetDirectionBetweenTiles(
- GET_TOWN_OR_INDUSTRY_TILE(fr.from),
- GET_TOWN_OR_INDUSTRY_TILE(fr.to)
- );
- _companies_ai[c->index].src.cargo = fr.cargo;
-
- // Fill the dest field
- _companies_ai[c->index].dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to);
- _companies_ai[c->index].dst.use_tile = 0;
- _companies_ai[c->index].dst.rand_rng = 7;
- _companies_ai[c->index].dst.cur_building_rule = 0xFF;
- _companies_ai[c->index].dst.unk6 = 1;
- _companies_ai[c->index].dst.unk7 = 0;
- _companies_ai[c->index].dst.buildcmd_a = 0xFF;
- _companies_ai[c->index].dst.buildcmd_b = 0xFF;
- _companies_ai[c->index].dst.direction = AiGetDirectionBetweenTiles(
- GET_TOWN_OR_INDUSTRY_TILE(fr.to),
- GET_TOWN_OR_INDUSTRY_TILE(fr.from)
- );
- _companies_ai[c->index].dst.cargo = fr.cargo;
-
- // Fill common fields
- _companies_ai[c->index].cargo_type = fr.cargo;
- _companies_ai[c->index].num_wagons = 2;
- _companies_ai[c->index].build_kind = 1;
- _companies_ai[c->index].num_build_rec = 2;
- _companies_ai[c->index].num_loco_to_build = 1;
- _companies_ai[c->index].num_want_fullload = 0;
- _companies_ai[c->index].wagon_list[0] = INVALID_VEHICLE;
- _companies_ai[c->index].order_list_blocks[0] = 0;
- _companies_ai[c->index].order_list_blocks[1] = 1;
- _companies_ai[c->index].order_list_blocks[2] = 255;
- _companies_ai[c->index].state = AIS_BUILD_DEFAULT_RAIL_BLOCKS;
- _companies_ai[c->index].state_mode = UCHAR_MAX;
- _companies_ai[c->index].state_counter = 0;
- _companies_ai[c->index].timeout_counter = 0;
-}
-
-static void AiWantTrainRoute(Company *c)
-{
- uint16 r = GB(Random(), 0, 16);
-
- _companies_ai[c->index].railtype_to_use = GetBestRailtype(c->index);
-
- if (r > 0xD000) {
- AiWantLongIndustryRoute(c);
- } else if (r > 0x6000) {
- AiWantMediumIndustryRoute(c);
- } else if (r > 0x1000) {
- AiWantShortIndustryRoute(c);
- } else if (r > 0x800) {
- AiWantPassengerRoute(c);
- } else {
- AiWantMailRoute(c);
- }
-}
-
-static void AiWantLongRoadIndustryRoute(Company *c)
-{
- int i;
- FoundRoute fr;
-
- i = 60;
- for (;;) {
- // look for one from the subsidy list
- AiFindSubsidyIndustryRoute(&fr);
- if (IsInsideMM(fr.distance, 35, 55 + 1)) break;
-
- // try a random one
- AiFindRandomIndustryRoute(&fr);
- if (IsInsideMM(fr.distance, 35, 55 + 1)) break;
-
- // only test 60 times
- if (--i == 0) return;
- }
-
- if (!AiCheckIfRouteIsGood(c, &fr, 2)) return;
-
- // Fill the source field
- _companies_ai[c->index].src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from);
- _companies_ai[c->index].src.use_tile = 0;
- _companies_ai[c->index].src.rand_rng = 9;
- _companies_ai[c->index].src.cur_building_rule = 0xFF;
- _companies_ai[c->index].src.buildcmd_a = 1;
- _companies_ai[c->index].src.direction = 0;
- _companies_ai[c->index].src.cargo = fr.cargo | 0x80;
-
- // Fill the dest field
- _companies_ai[c->index].dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to);
- _companies_ai[c->index].dst.use_tile = 0;
- _companies_ai[c->index].dst.rand_rng = 9;
- _companies_ai[c->index].dst.cur_building_rule = 0xFF;
- _companies_ai[c->index].dst.buildcmd_a = 0xFF;
- _companies_ai[c->index].dst.direction = 0;
- _companies_ai[c->index].dst.cargo = fr.cargo;
-
- // Fill common fields
- _companies_ai[c->index].cargo_type = fr.cargo;
- _companies_ai[c->index].num_build_rec = 2;
- _companies_ai[c->index].num_loco_to_build = 5;
- _companies_ai[c->index].num_want_fullload = 5;
-
-// _companies_ai[c->index].loco_id = INVALID_VEHICLE;
- _companies_ai[c->index].order_list_blocks[0] = 0;
- _companies_ai[c->index].order_list_blocks[1] = 1;
- _companies_ai[c->index].order_list_blocks[2] = 255;
-
- _companies_ai[c->index].state = AIS_BUILD_DEFAULT_ROAD_BLOCKS;
- _companies_ai[c->index].state_mode = UCHAR_MAX;
- _companies_ai[c->index].state_counter = 0;
- _companies_ai[c->index].timeout_counter = 0;
-}
-
-static void AiWantMediumRoadIndustryRoute(Company *c)
-{
- int i;
- FoundRoute fr;
-
- i = 60;
- for (;;) {
- // look for one from the subsidy list
- AiFindSubsidyIndustryRoute(&fr);
- if (IsInsideMM(fr.distance, 15, 40 + 1)) break;
-
- // try a random one
- AiFindRandomIndustryRoute(&fr);
- if (IsInsideMM(fr.distance, 15, 40 + 1)) break;
-
- // only test 60 times
- if (--i == 0) return;
- }
-
- if (!AiCheckIfRouteIsGood(c, &fr, 2)) return;
-
- // Fill the source field
- _companies_ai[c->index].src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from);
- _companies_ai[c->index].src.use_tile = 0;
- _companies_ai[c->index].src.rand_rng = 9;
- _companies_ai[c->index].src.cur_building_rule = 0xFF;
- _companies_ai[c->index].src.buildcmd_a = 1;
- _companies_ai[c->index].src.direction = 0;
- _companies_ai[c->index].src.cargo = fr.cargo | 0x80;
-
- // Fill the dest field
- _companies_ai[c->index].dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to);
- _companies_ai[c->index].dst.use_tile = 0;
- _companies_ai[c->index].dst.rand_rng = 9;
- _companies_ai[c->index].dst.cur_building_rule = 0xFF;
- _companies_ai[c->index].dst.buildcmd_a = 0xFF;
- _companies_ai[c->index].dst.direction = 0;
- _companies_ai[c->index].dst.cargo = fr.cargo;
-
- // Fill common fields
- _companies_ai[c->index].cargo_type = fr.cargo;
- _companies_ai[c->index].num_build_rec = 2;
- _companies_ai[c->index].num_loco_to_build = 3;
- _companies_ai[c->index].num_want_fullload = 3;
-
-// _companies_ai[c->index].loco_id = INVALID_VEHICLE;
- _companies_ai[c->index].order_list_blocks[0] = 0;
- _companies_ai[c->index].order_list_blocks[1] = 1;
- _companies_ai[c->index].order_list_blocks[2] = 255;
-
- _companies_ai[c->index].state = AIS_BUILD_DEFAULT_ROAD_BLOCKS;
- _companies_ai[c->index].state_mode = UCHAR_MAX;
- _companies_ai[c->index].state_counter = 0;
- _companies_ai[c->index].timeout_counter = 0;
-}
-
-static void AiWantLongRoadPassengerRoute(Company *c)
-{
- int i;
- FoundRoute fr;
-
- i = 60;
- for (;;) {
- // look for one from the subsidy list
- AiFindSubsidyPassengerRoute(&fr);
- if (IsInsideMM(fr.distance, 55, 180 + 1)) break;
-
- // try a random one
- AiFindRandomPassengerRoute(&fr);
- if (IsInsideMM(fr.distance, 55, 180 + 1)) break;
-
- // only test 60 times
- if (--i == 0) return;
- }
-
- fr.cargo = CT_PASSENGERS;
-
- if (!AiCheckIfRouteIsGood(c, &fr, 2)) return;
-
- // Fill the source field
- _companies_ai[c->index].src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to);
- _companies_ai[c->index].src.use_tile = 0;
- _companies_ai[c->index].src.rand_rng = 10;
- _companies_ai[c->index].src.cur_building_rule = 0xFF;
- _companies_ai[c->index].src.buildcmd_a = 1;
- _companies_ai[c->index].src.direction = 0;
- _companies_ai[c->index].src.cargo = CT_PASSENGERS;
-
- // Fill the dest field
- _companies_ai[c->index].dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from);
- _companies_ai[c->index].dst.use_tile = 0;
- _companies_ai[c->index].dst.rand_rng = 10;
- _companies_ai[c->index].dst.cur_building_rule = 0xFF;
- _companies_ai[c->index].dst.buildcmd_a = 0xFF;
- _companies_ai[c->index].dst.direction = 0;
- _companies_ai[c->index].dst.cargo = CT_PASSENGERS;
-
- // Fill common fields
- _companies_ai[c->index].cargo_type = CT_PASSENGERS;
- _companies_ai[c->index].num_build_rec = 2;
- _companies_ai[c->index].num_loco_to_build = 4;
- _companies_ai[c->index].num_want_fullload = 0;
-
-// _companies_ai[c->index].loco_id = INVALID_VEHICLE;
- _companies_ai[c->index].order_list_blocks[0] = 0;
- _companies_ai[c->index].order_list_blocks[1] = 1;
- _companies_ai[c->index].order_list_blocks[2] = 255;
-
- _companies_ai[c->index].state = AIS_BUILD_DEFAULT_ROAD_BLOCKS;
- _companies_ai[c->index].state_mode = UCHAR_MAX;
- _companies_ai[c->index].state_counter = 0;
- _companies_ai[c->index].timeout_counter = 0;
-}
-
-static void AiWantPassengerRouteInsideTown(Company *c)
-{
- int i;
- FoundRoute fr;
- Town *t;
-
- i = 60;
- for (;;) {
- // Find a town big enough
- t = AiFindRandomTown();
- if (t != NULL && t->population >= 700) break;
-
- // only test 60 times
- if (--i == 0) return;
- }
-
- fr.cargo = CT_PASSENGERS;
- fr.from = fr.to = t;
-
- if (!AiCheckIfRouteIsGood(c, &fr, 2)) return;
-
- // Fill the source field
- _companies_ai[c->index].src.spec_tile = t->xy;
- _companies_ai[c->index].src.use_tile = 0;
- _companies_ai[c->index].src.rand_rng = 10;
- _companies_ai[c->index].src.cur_building_rule = 0xFF;
- _companies_ai[c->index].src.buildcmd_a = 1;
- _companies_ai[c->index].src.direction = 0;
- _companies_ai[c->index].src.cargo = CT_PASSENGERS;
-
- // Fill the dest field
- _companies_ai[c->index].dst.spec_tile = t->xy;
- _companies_ai[c->index].dst.use_tile = 0;
- _companies_ai[c->index].dst.rand_rng = 10;
- _companies_ai[c->index].dst.cur_building_rule = 0xFF;
- _companies_ai[c->index].dst.buildcmd_a = 0xFF;
- _companies_ai[c->index].dst.direction = 0;
- _companies_ai[c->index].dst.cargo = CT_PASSENGERS;
-
- // Fill common fields
- _companies_ai[c->index].cargo_type = CT_PASSENGERS;
- _companies_ai[c->index].num_build_rec = 2;
- _companies_ai[c->index].num_loco_to_build = 2;
- _companies_ai[c->index].num_want_fullload = 0;
-
-// _companies_ai[c->index].loco_id = INVALID_VEHICLE;
- _companies_ai[c->index].order_list_blocks[0] = 0;
- _companies_ai[c->index].order_list_blocks[1] = 1;
- _companies_ai[c->index].order_list_blocks[2] = 255;
-
- _companies_ai[c->index].state = AIS_BUILD_DEFAULT_ROAD_BLOCKS;
- _companies_ai[c->index].state_mode = UCHAR_MAX;
- _companies_ai[c->index].state_counter = 0;
- _companies_ai[c->index].timeout_counter = 0;
-}
-
-static void AiWantRoadRoute(Company *c)
-{
- uint16 r = GB(Random(), 0, 16);
-
- if (r > 0x4000) {
- AiWantLongRoadIndustryRoute(c);
- } else if (r > 0x2000) {
- AiWantMediumRoadIndustryRoute(c);
- } else if (r > 0x1000) {
- AiWantLongRoadPassengerRoute(c);
- } else {
- AiWantPassengerRouteInsideTown(c);
- }
-}
-
-static void AiWantPassengerAircraftRoute(Company *c)
-{
- FoundRoute fr;
- int i;
-
- /* Get aircraft that would be bought for this route
- * (probably, as conditions may change before the route is fully built,
- * like running out of money and having to select different aircraft, etc ...) */
- EngineID veh = AiChooseAircraftToBuild(c->money, _companies_ai[c->index].build_kind != 0 ? AIR_CTOL : 0);
-
- /* No aircraft buildable mean no aircraft route */
- if (veh == INVALID_ENGINE) return;
-
- const AircraftVehicleInfo *avi = AircraftVehInfo(veh);
-
- /* For passengers, "optimal" number of days in transit is about 80 to 100
- * Calculate "maximum optimal number of squares" from speed for 80 days
- * 20 days should be enough for takeoff, land, taxi, etc ...
- *
- * "A vehicle traveling at 100kph will cross 5.6 tiles per day" ->
- * Since in table aircraft speeds are in "real km/h", this should be accurate
- * We get max_squares = avi->max_speed * 5.6 / 100.0 * 80 */
- int max_squares = avi->max_speed * 448 / 100;
-
- /* For example this will be 10456 tiles for 2334 km/h aircrafts with realistic aircraft speeds
- * and 836 with "unrealistic" speeds, much more than the original 95 squares limit
- *
- * Size of the map, if not rectangular, it is the larger dimension of it
- */
- int map_size = max(MapSizeX(), MapSizeY());
-
- /* Minimum distance between airports is half of map size, clamped between 1% and 20% of optimum.
- * May prevent building plane routes at all on small maps, but they will be ineffective there, so
- * it is feature, not a bug.
- * On smaller distances, buses or trains are usually more effective approach anyway.
- * Additional safeguard is needing at least 20 squares,
- * which may trigger in highly unusual configurations */
- int min_squares = max(20, max(max_squares / 100, min(max_squares / 5, map_size / 2)));
-
- /* Should not happen, unless aircraft with real speed under approx. 5 km/h is selected.
- * No such exist, unless using some NewGRF with ballons, zeppelins or similar
- * slow-moving stuff. In that case, bail out, it is faster to walk by foot anyway :). */
- if (max_squares < min_squares) return;
-
- i = 60;
- for (;;) {
-
- // look for one from the subsidy list
- AiFindSubsidyPassengerRoute(&fr);
- if (IsInsideMM(fr.distance, min_squares, max_squares + 1)) break;
-
- // try a random one
- AiFindRandomPassengerRoute(&fr);
- if (IsInsideMM(fr.distance, min_squares, max_squares + 1)) break;
-
- // only test 60 times
- if (--i == 0) return;
- }
-
- fr.cargo = CT_PASSENGERS;
- if (!AiCheckIfRouteIsGood(c, &fr, 4)) return;
-
-
- // Fill the source field
- _companies_ai[c->index].src.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.to);
- _companies_ai[c->index].src.use_tile = 0;
- _companies_ai[c->index].src.rand_rng = 12;
- _companies_ai[c->index].src.cur_building_rule = 0xFF;
- _companies_ai[c->index].src.cargo = fr.cargo;
-
- // Fill the dest field
- _companies_ai[c->index].dst.spec_tile = GET_TOWN_OR_INDUSTRY_TILE(fr.from);
- _companies_ai[c->index].dst.use_tile = 0;
- _companies_ai[c->index].dst.rand_rng = 12;
- _companies_ai[c->index].dst.cur_building_rule = 0xFF;
- _companies_ai[c->index].dst.cargo = fr.cargo;
-
- // Fill common fields
- _companies_ai[c->index].cargo_type = fr.cargo;
- _companies_ai[c->index].build_kind = 0;
- _companies_ai[c->index].num_build_rec = 2;
- _companies_ai[c->index].num_loco_to_build = 1;
- /* Using full load always may not be the best.
- * Pick random value and rely on selling the vehicle & route
- * afterwards if the choice was utterly wrong (or maybe altering the value if AI is improved)
- * When traffic is very low or very assymetric, is is better not to full load
- * When traffic is high, full/non-full make no difference
- * It should be better to run with aircraft only one way full 6 times per year,
- * rather than two way full 1 times.
- * Practical experiments with AI show that the non-full-load aircrafts are usually
- * those that survive
- * Also, non-full load is more resistant against starving (by building better stations
- * or using exclusive rights)
- */
- _companies_ai[c->index].num_want_fullload = Chance16(1, 5); // 20% chance
-// _companies_ai[c->index].loco_id = INVALID_VEHICLE;
- _companies_ai[c->index].order_list_blocks[0] = 0;
- _companies_ai[c->index].order_list_blocks[1] = 1;
- _companies_ai[c->index].order_list_blocks[2] = 255;
-
- _companies_ai[c->index].state = AIS_AIRPORT_STUFF;
- _companies_ai[c->index].timeout_counter = 0;
-}
-
-static void AiWantOilRigAircraftRoute(Company *c)
-{
- int i;
- FoundRoute fr;
- Town *t;
- Industry *in;
-
- i = 60;
- for (;;) {
- // Find a town
- t = AiFindRandomTown();
- if (t != NULL) {
- // Find a random oil rig industry
- in = AiFindRandomIndustry();
- if (in != NULL && GetIndustrySpec(in->type)->behaviour & INDUSTRYBEH_AI_AIRSHIP_ROUTES) {
- if (DistanceManhattan(t->xy, in->xy) < 60)
- break;
- }
- }
-
- // only test 60 times
- if (--i == 0) return;
- }
-
- fr.cargo = CT_PASSENGERS;
- fr.from = fr.to = t;
-
- if (!AiCheckIfRouteIsGood(c, &fr, 4)) return;
-
- // Fill the source field
- _companies_ai[c->index].src.spec_tile = t->xy;
- _companies_ai[c->index].src.use_tile = 0;
- _companies_ai[c->index].src.rand_rng = 12;
- _companies_ai[c->index].src.cur_building_rule = 0xFF;
- _companies_ai[c->index].src.cargo = CT_PASSENGERS;
-
- // Fill the dest field
- _companies_ai[c->index].dst.spec_tile = in->xy;
- _companies_ai[c->index].dst.use_tile = 0;
- _companies_ai[c->index].dst.rand_rng = 5;
- _companies_ai[c->index].dst.cur_building_rule = 0xFF;
- _companies_ai[c->index].dst.cargo = CT_PASSENGERS;
-
- // Fill common fields
- _companies_ai[c->index].cargo_type = CT_PASSENGERS;
- _companies_ai[c->index].build_kind = 1;
- _companies_ai[c->index].num_build_rec = 2;
- _companies_ai[c->index].num_loco_to_build = 1;
- _companies_ai[c->index].num_want_fullload = 0;
-// _companies_ai[c->index].loco_id = INVALID_VEHICLE;
- _companies_ai[c->index].order_list_blocks[0] = 0;
- _companies_ai[c->index].order_list_blocks[1] = 1;
- _companies_ai[c->index].order_list_blocks[2] = 255;
-
- _companies_ai[c->index].state = AIS_AIRPORT_STUFF;
- _companies_ai[c->index].timeout_counter = 0;
-}
-
-static void AiWantAircraftRoute(Company *c)
-{
- uint16 r = (uint16)Random();
-
- if (r >= 0x2AAA || _date < 0x3912 + DAYS_TILL_ORIGINAL_BASE_YEAR) {
- AiWantPassengerAircraftRoute(c);
- } else {
- AiWantOilRigAircraftRoute(c);
- }
-}
-
-
-
-static void AiStateWantNewRoute(Company *c)
-{
- uint16 r;
- int i;
-
- if (c->money < AiGetBasePrice(c) * 500) {
- _companies_ai[c->index].state = AIS_0;
- return;
- }
-
- i = 200;
- for (;;) {
- r = (uint16)Random();
-
- if (_settings_game.ai.ai_disable_veh_train &&
- _settings_game.ai.ai_disable_veh_roadveh &&
- _settings_game.ai.ai_disable_veh_aircraft &&
- _settings_game.ai.ai_disable_veh_ship) {
- return;
- }
-
- if (r < 0x7626) {
- if (_settings_game.ai.ai_disable_veh_train) continue;
- AiWantTrainRoute(c);
- } else if (r < 0xC4EA) {
- if (_settings_game.ai.ai_disable_veh_roadveh) continue;
- AiWantRoadRoute(c);
- } else if (r < 0xD89B) {
- if (_settings_game.ai.ai_disable_veh_aircraft) continue;
- AiWantAircraftRoute(c);
- } else {
- /* Ships are not implemented in this (broken) AI */
- }
-
- // got a route?
- if (_companies_ai[c->index].state != AIS_WANT_NEW_ROUTE) break;
-
- // time out?
- if (--i == 0) {
- if (++_companies_ai[c->index].state_counter == 556) _companies_ai[c->index].state = AIS_0;
- break;
- }
- }
-}
-
-static bool AiCheckTrackResources(TileIndex tile, const AiDefaultBlockData *p, byte cargo)
-{
- uint rad = (_settings_game.station.modified_catchment) ? CA_TRAIN : CA_UNMODIFIED;
-
- for (; p->mode != 4; p++) {
- AcceptedCargo values;
- TileIndex tile2;
- uint w;
- uint h;
-
- if (p->mode != 1) continue;
-
- tile2 = TILE_ADD(tile, ToTileIndexDiff(p->tileoffs));
- w = GB(p->attr, 1, 3);
- h = GB(p->attr, 4, 3);
-
- if (p->attr & 1) Swap(w, h);
-
- if (cargo & 0x80) {
- GetProductionAroundTiles(values, tile2, w, h, rad);
- return values[cargo & 0x7F] != 0;
- } else {
- GetAcceptanceAroundTiles(values, tile2, w, h, rad);
- if (!(values[cargo] & ~7))
- return false;
- if (cargo != CT_MAIL)
- return true;
- return !!((values[cargo] >> 1) & ~7);
- }
- }
-
- return true;
-}
-
-static CommandCost AiDoBuildDefaultRailTrack(TileIndex tile, const AiDefaultBlockData *p, RailType railtype, uint32 flag)
-{
- CommandCost ret;
- CommandCost total_cost(EXPENSES_CONSTRUCTION);
- Town *t = NULL;
- int rating = 0;
- int i, j, k;
-
- for (;;) {
- // This will seldomly overflow for valid reasons. Mask it to be on the safe side.
- uint c = TILE_MASK(tile + ToTileIndexDiff(p->tileoffs));
-
- _cleared_town = NULL;
-
- if (p->mode < 2) {
- if (p->mode == 0) {
- // Depot
- ret = DoCommand(c, railtype, p->attr, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_TRAIN_DEPOT);
- } else {
- // Station
- ret = DoCommand(c, (railtype & 0x0f) | (p->attr & 1) << 4 | (p->attr >> 4) << 8 | (p->attr >> 1 & 7) << 16, (INVALID_STATION << 16), flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_RAILROAD_STATION);
- }
-
- if (CmdFailed(ret)) return CMD_ERROR;
- total_cost.AddCost(ret);
-
-clear_town_stuff:;
- if (_cleared_town != NULL) {
- if (t != NULL && t != _cleared_town)
- return CMD_ERROR;
- t = _cleared_town;
- rating += _cleared_town_rating;
- }
- } else if (p->mode == 2) {
- /* Rail */
- if (IsTileType(c, MP_RAILWAY)) return CMD_ERROR;
-
- j = p->attr;
- k = 0;
-
- /* Build the rail
- * note: FOR_EACH_SET_BIT cannot be used here
- */
- for (i = 0; i != 6; i++, j >>= 1) {
- if (j & 1) {
- k = i;
- ret = DoCommand(c, railtype, i, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_SINGLE_RAIL);
- if (CmdFailed(ret)) return CMD_ERROR;
- total_cost.AddCost(ret);
- }
- }
-
- /* signals too? */
- if (j & 3) {
- // Can't build signals on a road.
- if (IsTileType(c, MP_ROAD)) return CMD_ERROR;
-
- if (flag & DC_EXEC) {
- j = 4 - j;
- do {
- ret = DoCommand(c, k, 0, flag, CMD_BUILD_SIGNALS);
- } while (--j);
- } else {
- ret.AddCost(_price.build_signals);
- }
- if (CmdFailed(ret)) return CMD_ERROR;
- total_cost.AddCost(ret);
- }
- } else if (p->mode == 3) {
- //Clear stuff and then build single rail.
- if (GetTileSlope(c, NULL) != SLOPE_FLAT) return CMD_ERROR;
- ret = DoCommand(c, 0, 0, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_LANDSCAPE_CLEAR);
- if (CmdFailed(ret)) return CMD_ERROR;
- total_cost.AddCost(ret);
- total_cost.AddCost(_price.build_rail);
-
- if (flag & DC_EXEC) {
- DoCommand(c, railtype, p->attr & 1, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_SINGLE_RAIL);
- }
-
- goto clear_town_stuff;
- } else {
- // Unk
- break;
- }
-
- p++;
- }
-
- if (!(flag & DC_EXEC)) {
- if (t != NULL && rating > t->ratings[_current_company]) {
- return CMD_ERROR;
- }
- }
-
- return total_cost;
-}
-
-// Returns rule and cost
-static int AiBuildDefaultRailTrack(TileIndex tile, byte p0, byte p1, byte p2, byte p3, byte dir, byte cargo, RailType railtype, CommandCost *cost)
-{
- int i;
- const AiDefaultRailBlock *p;
-
- for (i = 0; (p = _default_rail_track_data[i]) != NULL; i++) {
- if (p->p0 == p0 && p->p1 == p1 && p->p2 == p2 && p->p3 == p3 &&
- (p->dir == 0xFF || p->dir == dir || ((p->dir - 1) & 3) == dir)) {
- *cost = AiDoBuildDefaultRailTrack(tile, p->data, railtype, DC_NO_TOWN_RATING);
- if (CmdSucceeded(*cost) && AiCheckTrackResources(tile, p->data, cargo))
- return i;
- }
- }
-
- return -1;
-}
-
-static const byte _terraform_up_flags[] = {
- 14, 13, 12, 11,
- 10, 9, 8, 7,
- 6, 5, 4, 3,
- 2, 1, 0, 1,
- 2, 1, 4, 1,
- 2, 1, 8, 1,
- 2, 1, 4, 2,
- 2, 1
-};
-
-static const byte _terraform_down_flags[] = {
- 1, 2, 3, 4,
- 5, 6, 1, 8,
- 9, 10, 8, 12,
- 4, 2, 0, 0,
- 1, 2, 3, 4,
- 5, 6, 2, 8,
- 9, 10, 1, 12,
- 8, 4
-};
-
-static void AiDoTerraformLand(TileIndex tile, DiagDirection dir, int unk, int mode)
-{
- CompanyID old_company;
- uint32 r;
- Slope slope;
- uint h;
-
- old_company = _current_company;
- _current_company = OWNER_NONE;
-
- r = Random();
-
- unk &= (int)r;
-
- do {
- tile = TILE_MASK(tile + TileOffsByDiagDir(dir));
-
- r >>= 2;
- if (r & 2) {
- dir = ChangeDiagDir(dir, (r & 1) ? DIAGDIRDIFF_90LEFT : DIAGDIRDIFF_90RIGHT);
- }
- } while (--unk >= 0);
-
- slope = GetTileSlope(tile, &h);
-
- if (slope != SLOPE_FLAT) {
- if (mode > 0 || (mode == 0 && !(r & 0xC))) {
- // Terraform up
- DoCommand(tile, _terraform_up_flags[slope - 1], 1,
- DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_TERRAFORM_LAND);
- } else if (h != 0) {
- // Terraform down
- DoCommand(tile, _terraform_down_flags[slope - 1], 0,
- DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_TERRAFORM_LAND);
- }
- }
-
- _current_company = old_company;
-}
-
-static void AiStateBuildDefaultRailBlocks(Company *c)
-{
- uint i;
- int j;
- AiBuildRec *aib;
- int rule;
- CommandCost cost;
-
- // time out?
- if (++_companies_ai[c->index].timeout_counter == 1388) {
- _companies_ai[c->index].state = AIS_DELETE_RAIL_BLOCKS;
- return;
- }
-
- // do the following 8 times
- for (i = 0; i < 8; i++) {
- // check if we can build the default track
- aib = &_companies_ai[c->index].src;
- j = _companies_ai[c->index].num_build_rec;
- do {
- // this item has already been built?
- if (aib->cur_building_rule != 255) continue;
-
- // adjust the coordinate randomly,
- // to make sure that we find a position.
- aib->use_tile = AdjustTileCoordRandomly(aib->spec_tile, aib->rand_rng);
-
- // check if the track can be build there.
- rule = AiBuildDefaultRailTrack(aib->use_tile,
- _companies_ai[c->index].build_kind, _companies_ai[c->index].num_wagons,
- aib->unk6, aib->unk7,
- aib->direction, aib->cargo,
- _companies_ai[c->index].railtype_to_use,
- &cost
- );
-
- if (rule == -1) {
- // cannot build, terraform after a while
- if (_companies_ai[c->index].state_counter >= 600) {
- AiDoTerraformLand(aib->use_tile, (DiagDirection)(Random() & 3), 3, (int8)_companies_ai[c->index].state_mode);
- }
- // also try the other terraform direction
- if (++_companies_ai[c->index].state_counter >= 1000) {
- _companies_ai[c->index].state_counter = 0;
- _companies_ai[c->index].state_mode = -_companies_ai[c->index].state_mode;
- }
- } else if (CheckCompanyHasMoney(cost)) {
- // company has money, build it.
- aib->cur_building_rule = rule;
-
- AiDoBuildDefaultRailTrack(
- aib->use_tile,
- _default_rail_track_data[rule]->data,
- _companies_ai[c->index].railtype_to_use,
- DC_EXEC | DC_NO_TOWN_RATING
- );
- }
- } while (++aib, --j);
- }
-
- // check if we're done with all of them
- aib = &_companies_ai[c->index].src;
- j = _companies_ai[c->index].num_build_rec;
- do {
- if (aib->cur_building_rule == 255) return;
- } while (++aib, --j);
-
- // yep, all are done. switch state to the rail building state.
- _companies_ai[c->index].state = AIS_BUILD_RAIL;
- _companies_ai[c->index].state_mode = 255;
-}
-
-static TileIndex AiGetEdgeOfDefaultRailBlock(byte rule, TileIndex tile, byte cmd, DiagDirection *dir)
-{
- const AiDefaultBlockData *p = _default_rail_track_data[rule]->data;
-
- while (p->mode != 3 || !((--cmd) & 0x80)) p++;
-
- return tile + ToTileIndexDiff(p->tileoffs) - TileOffsByDiagDir(*dir = p->attr);
-}
-
-struct AiRailPathFindData {
- TileIndex tile;
- TileIndex tile2;
- int count;
- bool flag;
-};
-
-static bool AiEnumFollowTrack(TileIndex tile, AiRailPathFindData *a, int track, uint length)
-{
- if (a->flag) return true;
-
- if (length > 20 || tile == a->tile) {
- a->flag = true;
- return true;
- }
-
- if (DistanceMax(tile, a->tile2) < 4) a->count++;
-
- return false;
-}
-
-static bool AiDoFollowTrack(const Company *c)
-{
- AiRailPathFindData arpfd;
-
- arpfd.tile = _companies_ai[c->index].start_tile_a;
- arpfd.tile2 = _companies_ai[c->index].cur_tile_a;
- arpfd.flag = false;
- arpfd.count = 0;
- FollowTrack(_companies_ai[c->index].cur_tile_a + TileOffsByDiagDir(_companies_ai[c->index].cur_dir_a), PATHFIND_FLAGS_NONE, TRANSPORT_RAIL, 0, ReverseDiagDir(_companies_ai[c->index].cur_dir_a),
- (TPFEnumProc*)AiEnumFollowTrack, NULL, &arpfd);
- return arpfd.count > 8;
-}
-
-struct AiRailFinder {
- TileIndex final_tile;
- DiagDirection final_dir;
- byte depth;
- byte recursive_mode;
- DiagDirection cur_best_dir;
- DiagDirection best_dir;
- byte cur_best_depth;
- byte best_depth;
- uint cur_best_dist;
- const byte *best_ptr;
- uint best_dist;
- TileIndex cur_best_tile, best_tile;
- TileIndex bridge_end_tile;
- Company *company;
-};
-
-static const byte _ai_table_15[4][8] = {
- {0, 0, 4, 3, 3, 1, 128 + 0, 64},
- {1, 1, 2, 0, 4, 2, 128 + 1, 65},
- {0, 2, 2, 3, 5, 1, 128 + 2, 66},
- {1, 3, 5, 0, 3, 2, 128 + 3, 67}
-};
-
-
-static bool AiIsTileBanned(const Company *c, TileIndex tile, byte val)
-{
- int i;
-
- for (i = 0; i != _companies_ai[c->index].banned_tile_count; i++) {
- if (_companies_ai[c->index].banned_tiles[i] == tile && _companies_ai[c->index].banned_val[i] == val) {
- return true;
- }
- }
- return false;
-}
-
-static void AiBanTile(Company *c, TileIndex tile, byte val)
-{
- uint i;
-
- for (i = lengthof(_companies_ai[c->index].banned_tiles) - 1; i != 0; i--) {
- _companies_ai[c->index].banned_tiles[i] = _companies_ai[c->index].banned_tiles[i - 1];
- _companies_ai[c->index].banned_val[i] = _companies_ai[c->index].banned_val[i - 1];
- }
-
- _companies_ai[c->index].banned_tiles[0] = tile;
- _companies_ai[c->index].banned_val[0] = val;
-
- if (_companies_ai[c->index].banned_tile_count != lengthof(_companies_ai[c->index].banned_tiles)) {
- _companies_ai[c->index].banned_tile_count++;
- }
-}
-
-static void AiBuildRailRecursive(AiRailFinder *arf, TileIndex tile, DiagDirection dir);
-
-static bool AiCheckRailPathBetter(AiRailFinder *arf, const byte *p)
-{
- bool better = false;
-
- if (arf->recursive_mode < 1) {
- // Mode is 0. This means destination has not been found yet.
- // If the found path is shorter than the current one, remember it.
- if (arf->cur_best_dist < arf->best_dist) {
- arf->best_dir = arf->cur_best_dir;
- arf->best_dist = arf->cur_best_dist;
- arf->best_ptr = p;
- arf->best_tile = arf->cur_best_tile;
- better = true;
- }
- } else if (arf->recursive_mode > 1) {
- // Mode is 2.
- if (arf->best_dist != 0 || arf->cur_best_depth < arf->best_depth) {
- arf->best_depth = arf->cur_best_depth;
- arf->best_dist = 0;
- arf->best_ptr = p;
- arf->best_tile = 0;
- better = true;
- }
- }
- arf->recursive_mode = 0;
- arf->cur_best_dist = UINT_MAX;
- arf->cur_best_depth = 0xff;
-
- return better;
-}
-
-static inline void AiCheckBuildRailBridgeHere(AiRailFinder *arf, TileIndex tile, const byte *p)
-{
- Slope tileh;
- uint z;
- bool flag;
-
- DiagDirection dir2 = (DiagDirection)(p[0] & 3);
-
- tileh = GetTileSlope(tile, &z);
- if (tileh == InclinedSlope(ReverseDiagDir(dir2)) || (tileh == SLOPE_FLAT && z != 0)) {
- TileIndex tile_new = tile;
-
- // Allow bridges directly over bottom tiles
- flag = z == 0;
- for (;;) {
- TileType type;
-
- if ((TileIndexDiff)tile_new < -TileOffsByDiagDir(dir2)) return; // Wraping around map, no bridge possible!
- tile_new = TILE_MASK(tile_new + TileOffsByDiagDir(dir2));
- type = GetTileType(tile_new);
-
- if (type == MP_CLEAR || type == MP_TREES || GetTileSlope(tile_new, NULL) != SLOPE_FLAT) {
- if (!flag) return;
- break;
- }
- if (type != MP_WATER && type != MP_RAILWAY && type != MP_ROAD) return;
- flag = true;
- }
-
- // Is building a (rail)bridge possible at this place (type doesn't matter)?
- if (CmdFailed(DoCommand(tile_new, tile, _companies_ai[arf->company->index].railtype_to_use << 8 | TRANSPORT_RAIL << 15, DC_AUTO, CMD_BUILD_BRIDGE))) {
- return;
- }
- AiBuildRailRecursive(arf, tile_new, dir2);
-
- // At the bottom depth, check if the new path is better than the old one.
- if (arf->depth == 1) {
- if (AiCheckRailPathBetter(arf, p)) arf->bridge_end_tile = tile_new;
- }
- }
-}
-
-static inline void AiCheckBuildRailTunnelHere(AiRailFinder *arf, TileIndex tile, const byte *p)
-{
- uint z;
-
- if (GetTileSlope(tile, &z) == InclinedSlope((DiagDirection)(p[0] & 3)) && z != 0) {
- CommandCost cost = DoCommand(tile, _companies_ai[arf->company->index].railtype_to_use, 0, DC_AUTO, CMD_BUILD_TUNNEL);
-
- if (CmdSucceeded(cost) && cost.GetCost() <= (arf->company->money >> 4)) {
- AiBuildRailRecursive(arf, _build_tunnel_endtile, (DiagDirection)(p[0] & 3));
- if (arf->depth == 1) AiCheckRailPathBetter(arf, p);
- }
- }
-}
-
-
-static void AiBuildRailRecursive(AiRailFinder *arf, TileIndex tile, DiagDirection dir)
-{
- const byte *p;
-
- tile = TILE_MASK(tile + TileOffsByDiagDir(dir));
-
- // Reached destination?
- if (tile == arf->final_tile) {
- if (arf->final_dir != ReverseDiagDir(dir)) {
- if (arf->recursive_mode != 2) arf->recursive_mode = 1;
- } else if (arf->recursive_mode != 2) {
- arf->recursive_mode = 2;
- arf->cur_best_depth = arf->depth;
- } else {
- if (arf->depth < arf->cur_best_depth) arf->cur_best_depth = arf->depth;
- }
- return;
- }
-
- // Depth too deep?
- if (arf->depth >= 4) {
- uint dist = DistanceMaxPlusManhattan(tile, arf->final_tile);
-
- if (dist < arf->cur_best_dist) {
- // Store the tile that is closest to the final position.
- arf->cur_best_depth = arf->depth;
- arf->cur_best_dist = dist;
- arf->cur_best_tile = tile;
- arf->cur_best_dir = dir;
- }
- return;
- }
-
- // Increase recursion depth
- arf->depth++;
-
- // Grab pointer to list of stuff that is possible to build
- p = _ai_table_15[dir];
-
- // Try to build a single rail in all directions.
- if (GetTileZ(tile) == 0) {
- p += 6;
- } else {
- do {
- // Make sure the tile is not in the list of banned tiles and that a rail can be built here.
- if (!AiIsTileBanned(arf->company, tile, p[0]) &&
- CmdSucceeded(DoCommand(tile, _companies_ai[arf->company->index].railtype_to_use, p[0], DC_AUTO | DC_NO_WATER | DC_NO_RAIL_OVERLAP, CMD_BUILD_SINGLE_RAIL))) {
- AiBuildRailRecursive(arf, tile, (DiagDirection)p[1]);
- }
-
- // At the bottom depth?
- if (arf->depth == 1) AiCheckRailPathBetter(arf, p);
-
- p += 2;
- } while (!(p[0] & 0x80));
- }
-
- AiCheckBuildRailBridgeHere(arf, tile, p);
- AiCheckBuildRailTunnelHere(arf, tile, p + 1);
-
- arf->depth--;
-}
-
-
-static void AiBuildRailConstruct(Company *c)
-{
- AiRailFinder arf;
- int i;
-
- // Check too much lookahead?
- if (AiDoFollowTrack(c)) {
- _companies_ai[c->index].state_counter = (Random()&0xE)+6; // Destruct this amount of blocks
- _companies_ai[c->index].state_mode = 1; // Start destruct
-
- // Ban this tile and don't reach it for a while.
- AiBanTile(c, _companies_ai[c->index].cur_tile_a, FindFirstBit(GetRailTrackStatus(_companies_ai[c->index].cur_tile_a)));
- return;
- }
-
- // Setup recursive finder and call it.
- arf.company = c;
- arf.final_tile = _companies_ai[c->index].cur_tile_b;
- arf.final_dir = _companies_ai[c->index].cur_dir_b;
- arf.depth = 0;
- arf.recursive_mode = 0;
- arf.best_ptr = NULL;
- arf.cur_best_dist = UINT_MAX;
- arf.cur_best_depth = 0xff;
- arf.best_dist = UINT_MAX;
- arf.best_depth = 0xff;
- arf.cur_best_tile = 0;
- arf.best_tile = 0;
- AiBuildRailRecursive(&arf, _companies_ai[c->index].cur_tile_a, _companies_ai[c->index].cur_dir_a);
-
- // Reached destination?
- if (arf.recursive_mode == 2 && arf.cur_best_depth == 0) {
- _companies_ai[c->index].state_mode = 255;
- return;
- }
-
- // Didn't find anything to build?
- if (arf.best_ptr == NULL) {
- // Terraform some
- for (i = 0; i != 5; i++) {
- AiDoTerraformLand(_companies_ai[c->index].cur_tile_a, _companies_ai[c->index].cur_dir_a, 3, 0);
- }
-
- if (++_companies_ai[c->index].state_counter == 21) {
- _companies_ai[c->index].state_counter = 40;
- _companies_ai[c->index].state_mode = 1;
-
- // Ban this tile
- AiBanTile(c, _companies_ai[c->index].cur_tile_a, FindFirstBit(GetRailTrackStatus(_companies_ai[c->index].cur_tile_a)));
- }
- return;
- }
-
- _companies_ai[c->index].cur_tile_a += TileOffsByDiagDir(_companies_ai[c->index].cur_dir_a);
-
- if (arf.best_ptr[0] & 0x80) {
- TileIndex t1 = _companies_ai[c->index].cur_tile_a;
- TileIndex t2 = arf.bridge_end_tile;
-
- int32 bridge_len = GetTunnelBridgeLength(t1, t2);
-
- DiagDirection dir = (TileX(t1) == TileX(t2) ? DIAGDIR_SE : DIAGDIR_SW);
- Track track = DiagDirToDiagTrack(dir);
-
- if (t2 < t1) dir = ReverseDiagDir(dir);
-
- /* try to build a long rail instead of bridge... */
- bool fail = false;
- CommandCost cost;
- TileIndex t = t1;
-
- /* try to build one rail on each tile - can't use CMD_BUILD_RAILROAD_TRACK now, it can build one part of track without failing */
- do {
- cost = DoCommand(t, _companies_ai[c->index].railtype_to_use, track, DC_AUTO | DC_NO_WATER, CMD_BUILD_SINGLE_RAIL);
- /* do not allow building over existing track */
- if (CmdFailed(cost) || IsTileType(t, MP_RAILWAY)) {
- fail = true;
- break;
- }
- t += TileOffsByDiagDir(dir);
- } while (t != t2);
-
- /* can we build long track? */
- if (!fail) cost = DoCommand(t1, t2, _companies_ai[c->index].railtype_to_use | (track << 4), DC_AUTO | DC_NO_WATER, CMD_BUILD_RAILROAD_TRACK);
-
- if (!fail && CmdSucceeded(cost) && cost.GetCost() <= c->money) {
- DoCommand(t1, t2, _companies_ai[c->index].railtype_to_use | (track << 4), DC_AUTO | DC_NO_WATER | DC_EXEC, CMD_BUILD_RAILROAD_TRACK);
- } else {
-
- /* Figure out which (rail)bridge type to build
- * start with best bridge, then go down to worse and worse bridges
- * unnecessary to check for worst bridge (i=0), since AI will always build that. */
- int i;
- for (i = MAX_BRIDGES - 1; i != 0; i--) {
- if (CheckBridge_Stuff(i, bridge_len)) {
- CommandCost cost = DoCommand(t1, t2, i | _companies_ai[c->index].railtype_to_use << 8 | TRANSPORT_RAIL << 15, DC_AUTO, CMD_BUILD_BRIDGE);
- if (CmdSucceeded(cost) && cost.GetCost() < (c->money >> 1) && cost.GetCost() < ((c->money + _economy.max_loan - c->current_loan) >> 5)) break;
- }
- }
-
- /* Build it */
- DoCommand(t1, t2, i | _companies_ai[c->index].railtype_to_use << 8 | TRANSPORT_RAIL << 15, DC_AUTO | DC_EXEC, CMD_BUILD_BRIDGE);
- }
-
- _companies_ai[c->index].cur_tile_a = t2;
- _companies_ai[c->index].state_counter = 0;
- } else if (arf.best_ptr[0] & 0x40) {
- // tunnel
- DoCommand(_companies_ai[c->index].cur_tile_a, _companies_ai[c->index].railtype_to_use, 0, DC_AUTO | DC_EXEC, CMD_BUILD_TUNNEL);
- _companies_ai[c->index].cur_tile_a = _build_tunnel_endtile;
- _companies_ai[c->index].state_counter = 0;
- } else {
- // rail
- _companies_ai[c->index].cur_dir_a = (DiagDirection)(arf.best_ptr[1] & 3);
- DoCommand(_companies_ai[c->index].cur_tile_a, _companies_ai[c->index].railtype_to_use, arf.best_ptr[0],
- DC_EXEC | DC_AUTO | DC_NO_WATER | DC_NO_RAIL_OVERLAP, CMD_BUILD_SINGLE_RAIL);
- _companies_ai[c->index].state_counter = 0;
- }
-
- if (arf.best_tile != 0) {
- for (i = 0; i != 2; i++) {
- AiDoTerraformLand(arf.best_tile, arf.best_dir, 3, 0);
- }
- }
-}
-
-static bool AiRemoveTileAndGoForward(Company *c)
-{
- const byte *ptr;
- TileIndex tile = _companies_ai[c->index].cur_tile_a;
- TileIndex tilenew;
-
- if (IsTileType(tile, MP_TUNNELBRIDGE)) {
- if (IsTunnel(tile)) {
- // Clear the tunnel and continue at the other side of it.
- if (CmdFailed(DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR)))
- return false;
- _companies_ai[c->index].cur_tile_a = TILE_MASK(_build_tunnel_endtile - TileOffsByDiagDir(_companies_ai[c->index].cur_dir_a));
- return true;
- } else { // IsBridge(tile)
- // Check if the bridge points in the right direction.
- // This is not really needed the first place AiRemoveTileAndGoForward is called.
- if (DiagDirToAxis(GetTunnelBridgeDirection(tile)) != (_companies_ai[c->index].cur_dir_a & 1)) return false;
-
- tile = GetOtherBridgeEnd(tile);
-
- tilenew = TILE_MASK(tile - TileOffsByDiagDir(_companies_ai[c->index].cur_dir_a));
- // And clear the bridge.
- if (CmdFailed(DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR)))
- return false;
- _companies_ai[c->index].cur_tile_a = tilenew;
- return true;
- }
- }
-
- // Find the railtype at the position. Quit if no rail there.
- TrackBits bits = GetRailTrackStatus(tile) & DiagdirReachesTracks(ReverseDiagDir(_companies_ai[c->index].cur_dir_a));
- if (bits == TRACK_BIT_NONE) return false;
-
- // Convert into a bit position that CMD_REMOVE_SINGLE_RAIL expects.
- Track track = FindFirstTrack(bits);
-
- // Then remove and signals if there are any.
- if (IsTileType(tile, MP_RAILWAY) &&
- GetRailTileType(tile) == RAIL_TILE_SIGNALS) {
- DoCommand(tile, 0, 0, DC_EXEC, CMD_REMOVE_SIGNALS);
- }
-
- // And also remove the rail.
- if (CmdFailed(DoCommand(tile, 0, track, DC_EXEC, CMD_REMOVE_SINGLE_RAIL)))
- return false;
-
- // Find the direction at the other edge of the rail.
- ptr = _ai_table_15[ReverseDiagDir(_companies_ai[c->index].cur_dir_a)];
- while (ptr[0] != track) ptr += 2;
- _companies_ai[c->index].cur_dir_a = ReverseDiagDir((DiagDirection)ptr[1]);
-
- // And then also switch tile.
- _companies_ai[c->index].cur_tile_a = TILE_MASK(_companies_ai[c->index].cur_tile_a - TileOffsByDiagDir(_companies_ai[c->index].cur_dir_a));
-
- return true;
-}
-
-
-static void AiBuildRailDestruct(Company *c)
-{
- // Decrease timeout.
- if (!--_companies_ai[c->index].state_counter) {
- _companies_ai[c->index].state_mode = 2;
- _companies_ai[c->index].state_counter = 0;
- }
-
- // Don't do anything if the destination is already reached.
- if (_companies_ai[c->index].cur_tile_a == _companies_ai[c->index].start_tile_a) return;
-
- AiRemoveTileAndGoForward(c);
-}
-
-
-static void AiBuildRail(Company *c)
-{
- switch (_companies_ai[c->index].state_mode) {
- case 0: // Construct mode, build new rail.
- AiBuildRailConstruct(c);
- break;
-
- case 1: // Destruct mode, destroy the rail currently built.
- AiBuildRailDestruct(c);
- break;
-
- case 2: {
- uint i;
-
- // Terraform some and then try building again.
- for (i = 0; i != 4; i++) {
- AiDoTerraformLand(_companies_ai[c->index].cur_tile_a, _companies_ai[c->index].cur_dir_a, 3, 0);
- }
-
- if (++_companies_ai[c->index].state_counter == 4) {
- _companies_ai[c->index].state_counter = 0;
- _companies_ai[c->index].state_mode = 0;
- }
- }
-
- default: break;
- }
-}
-
-static void AiStateBuildRail(Company *c)
-{
- int num;
- AiBuildRec *aib;
- byte cmd;
- TileIndex tile;
- DiagDirection dir;
-
- // time out?
- if (++_companies_ai[c->index].timeout_counter == 1388) {
- _companies_ai[c->index].state = AIS_DELETE_RAIL_BLOCKS;
- return;
- }
-
- // Currently building a rail between two points?
- if (_companies_ai[c->index].state_mode != 255) {
- AiBuildRail(c);
-
- // Alternate between edges
- Swap(_companies_ai[c->index].start_tile_a, _companies_ai[c->index].start_tile_b);
- Swap(_companies_ai[c->index].cur_tile_a, _companies_ai[c->index].cur_tile_b);
- Swap(_companies_ai[c->index].start_dir_a, _companies_ai[c->index].start_dir_b);
- Swap(_companies_ai[c->index].cur_dir_a, _companies_ai[c->index].cur_dir_b);
- return;
- }
-
- // Now, find two new points to build between
- num = _companies_ai[c->index].num_build_rec;
- aib = &_companies_ai[c->index].src;
-
- for (;;) {
- cmd = aib->buildcmd_a;
- aib->buildcmd_a = 255;
- if (cmd != 255) break;
-
- cmd = aib->buildcmd_b;
- aib->buildcmd_b = 255;
- if (cmd != 255) break;
-
- aib++;
- if (--num == 0) {
- _companies_ai[c->index].state = AIS_BUILD_RAIL_VEH;
- _companies_ai[c->index].state_counter = 0; // timeout
- return;
- }
- }
-
- // Find first edge to build from.
- tile = AiGetEdgeOfDefaultRailBlock(aib->cur_building_rule, aib->use_tile, cmd & 3, &dir);
- _companies_ai[c->index].start_tile_a = tile;
- _companies_ai[c->index].cur_tile_a = tile;
- _companies_ai[c->index].start_dir_a = dir;
- _companies_ai[c->index].cur_dir_a = dir;
- DoCommand(TILE_MASK(tile + TileOffsByDiagDir(dir)), 0, (dir & 1) ? 1 : 0, DC_EXEC, CMD_REMOVE_SINGLE_RAIL);
-
- assert(TILE_MASK(tile) != 0xFF00);
-
- // Find second edge to build to
- aib = (&_companies_ai[c->index].src) + ((cmd >> 4) & 0xF);
- tile = AiGetEdgeOfDefaultRailBlock(aib->cur_building_rule, aib->use_tile, (cmd >> 2) & 3, &dir);
- _companies_ai[c->index].start_tile_b = tile;
- _companies_ai[c->index].cur_tile_b = tile;
- _companies_ai[c->index].start_dir_b = dir;
- _companies_ai[c->index].cur_dir_b = dir;
- DoCommand(TILE_MASK(tile + TileOffsByDiagDir(dir)), 0, (dir & 1) ? 1 : 0, DC_EXEC, CMD_REMOVE_SINGLE_RAIL);
-
- assert(TILE_MASK(tile) != 0xFF00);
-
- // And setup state.
- _companies_ai[c->index].state_mode = 2;
- _companies_ai[c->index].state_counter = 0;
- _companies_ai[c->index].banned_tile_count = 0;
-}
-
-static StationID AiGetStationIdByDef(TileIndex tile, int id)
-{
- const AiDefaultBlockData *p = _default_rail_track_data[id]->data;
- while (p->mode != 1) p++;
- return GetStationIndex(TILE_ADD(tile, ToTileIndexDiff(p->tileoffs)));
-}
-
-static EngineID AiFindBestWagon(CargoID cargo, RailType railtype)
-{
- EngineID best_veh_index = INVALID_ENGINE;
- uint16 best_capacity = 0;
- uint16 best_speed = 0;
- uint speed;
- const Engine *e;
-
- FOR_ALL_ENGINES_OF_TYPE(e, VEH_TRAIN) {
- EngineID i = e->index;
- const RailVehicleInfo *rvi = &e->u.rail;
-
- if (!IsCompatibleRail(rvi->railtype, railtype) ||
- rvi->railveh_type != RAILVEH_WAGON ||
- !HasBit(e->company_avail, _current_company)) {
- continue;
- }
-
- if (rvi->cargo_type != cargo) continue;
-
- /* max_speed of 0 indicates no speed limit */
- speed = rvi->max_speed == 0 ? 0xFFFF : rvi->max_speed;
-
- if (rvi->capacity >= best_capacity && speed >= best_speed) {
- best_capacity = rvi->capacity;
- best_speed = best_speed;
- best_veh_index = i;
- }
- }
-
- return best_veh_index;
-}
-
-static void AiStateBuildRailVeh(Company *c)
-{
- const AiDefaultBlockData *ptr;
- TileIndex tile;
- EngineID veh;
- int i;
- CargoID cargo;
- CommandCost cost;
- Vehicle *v;
- VehicleID loco_id;
-
- ptr = _default_rail_track_data[_companies_ai[c->index].src.cur_building_rule]->data;
- while (ptr->mode != 0) ptr++;
-
- tile = TILE_ADD(_companies_ai[c->index].src.use_tile, ToTileIndexDiff(ptr->tileoffs));
-
-
- cargo = _companies_ai[c->index].cargo_type;
- for (i = 0;;) {
- if (_companies_ai[c->index].wagon_list[i] == INVALID_VEHICLE) {
- veh = AiFindBestWagon(cargo, _companies_ai[c->index].railtype_to_use);
- /* veh will return INVALID_ENGINE if no suitable wagon is available.
- * We shall treat this in the same way as having no money */
- if (veh == INVALID_ENGINE) goto handle_nocash;
- cost = DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_RAIL_VEHICLE);
- if (CmdFailed(cost)) goto handle_nocash;
- _companies_ai[c->index].wagon_list[i] = _new_vehicle_id;
- _companies_ai[c->index].wagon_list[i + 1] = INVALID_VEHICLE;
- return;
- }
- if (cargo == CT_MAIL) cargo = CT_PASSENGERS;
- if (++i == _companies_ai[c->index].num_wagons * 2 - 1) break;
- }
-
- // Which locomotive to build?
- veh = AiChooseTrainToBuild(_companies_ai[c->index].railtype_to_use, c->money, cargo != CT_PASSENGERS ? 1 : 0, tile);
- if (veh == INVALID_ENGINE) {
-handle_nocash:
- // after a while, if AI still doesn't have cash, get out of this block by selling the wagons.
- if (++_companies_ai[c->index].state_counter == 1000) {
- for (i = 0; _companies_ai[c->index].wagon_list[i] != INVALID_VEHICLE; i++) {
- cost = DoCommand(tile, _companies_ai[c->index].wagon_list[i], 0, DC_EXEC, CMD_SELL_RAIL_WAGON);
- assert(CmdSucceeded(cost));
- }
- _companies_ai[c->index].state = AIS_0;
- }
- return;
- }
-
- // Try to build the locomotive
- cost = DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_RAIL_VEHICLE);
- assert(CmdSucceeded(cost));
- loco_id = _new_vehicle_id;
-
- // Sell a vehicle if the train is double headed.
- v = GetVehicle(loco_id);
- if (v->Next() != NULL) {
- i = _companies_ai[c->index].wagon_list[_companies_ai[c->index].num_wagons * 2 - 2];
- _companies_ai[c->index].wagon_list[_companies_ai[c->index].num_wagons * 2 - 2] = INVALID_VEHICLE;
- DoCommand(tile, i, 0, DC_EXEC, CMD_SELL_RAIL_WAGON);
- }
-
- // Move the wagons onto the train
- for (i = 0; _companies_ai[c->index].wagon_list[i] != INVALID_VEHICLE; i++) {
- DoCommand(tile, _companies_ai[c->index].wagon_list[i] | (loco_id << 16), 0, DC_EXEC, CMD_MOVE_RAIL_VEHICLE);
- }
-
- for (i = 0; _companies_ai[c->index].order_list_blocks[i] != 0xFF; i++) {
- const AiBuildRec *aib = &_companies_ai[c->index].src + _companies_ai[c->index].order_list_blocks[i];
- bool is_pass = (
- _companies_ai[c->index].cargo_type == CT_PASSENGERS ||
- _companies_ai[c->index].cargo_type == CT_MAIL ||
- (_settings_game.game_creation.landscape == LT_TEMPERATE && _companies_ai[c->index].cargo_type == CT_VALUABLES)
- );
- Order order;
-
- order.MakeGoToStation(AiGetStationIdByDef(aib->use_tile, aib->cur_building_rule));
-
- if (!is_pass && i == 1) order.SetUnloadType(OUFB_UNLOAD);
- if (_companies_ai[c->index].num_want_fullload != 0 && (is_pass || i == 0))
- order.SetLoadType(OLFB_FULL_LOAD);
-
- DoCommand(0, loco_id + (i << 16), order.Pack(), DC_EXEC, CMD_INSERT_ORDER);
- }
-
- DoCommand(0, loco_id, 0, DC_EXEC, CMD_START_STOP_VEHICLE);
-
- DoCommand(0, loco_id, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT);
-
- if (_companies_ai[c->index].num_want_fullload != 0) _companies_ai[c->index].num_want_fullload--;
-
- if (--_companies_ai[c->index].num_loco_to_build != 0) {
-// _companies_ai[c->index].loco_id = INVALID_VEHICLE;
- _companies_ai[c->index].wagon_list[0] = INVALID_VEHICLE;
- } else {
- _companies_ai[c->index].state = AIS_0;
- }
-}
-
-static void AiStateDeleteRailBlocks(Company *c)
-{
- const AiBuildRec *aib = &_companies_ai[c->index].src;
- uint num = _companies_ai[c->index].num_build_rec;
-
- do {
- const AiDefaultBlockData *b;
-
- if (aib->cur_building_rule == 255) continue;
- for (b = _default_rail_track_data[aib->cur_building_rule]->data; b->mode != 4; b++) {
- DoCommand(TILE_ADD(aib->use_tile, ToTileIndexDiff(b->tileoffs)), 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
- }
- } while (++aib, --num);
-
- _companies_ai[c->index].state = AIS_0;
-}
-
-static bool AiCheckRoadResources(TileIndex tile, const AiDefaultBlockData *p, byte cargo)
-{
- uint values[NUM_CARGO];
- int rad;
-
- if (_settings_game.station.modified_catchment) {
- rad = CA_TRUCK; // Same as CA_BUS at the moment?
- } else { // change that at some point?
- rad = 4;
- }
-
- for (;; p++) {
- if (p->mode == 4) {
- return true;
- } else if (p->mode == 1) {
- TileIndex tile2 = TILE_ADD(tile, ToTileIndexDiff(p->tileoffs));
-
- if (cargo & 0x80) {
- GetProductionAroundTiles(values, tile2, 1, 1, rad);
- return values[cargo & 0x7F] != 0;
- } else {
- GetAcceptanceAroundTiles(values, tile2, 1, 1, rad);
- return (values[cargo]&~7) != 0;
- }
- }
- }
-}
-
-static bool _want_road_truck_station;
-static CommandCost AiDoBuildDefaultRoadBlock(TileIndex tile, const AiDefaultBlockData *p, uint32 flag);
-
-// Returns rule and cost
-static int AiFindBestDefaultRoadBlock(TileIndex tile, byte direction, byte cargo, CommandCost *cost)
-{
- int i;
- const AiDefaultRoadBlock *p;
-
- _want_road_truck_station = (cargo & 0x7F) != CT_PASSENGERS;
-
- for (i = 0; (p = _road_default_block_data[i]) != NULL; i++) {
- if (p->dir == direction) {
- *cost = AiDoBuildDefaultRoadBlock(tile, p->data, 0);
- if (CmdSucceeded(*cost) && AiCheckRoadResources(tile, p->data, cargo))
- return i;
- }
- }
-
- return -1;
-}
-
-static CommandCost AiDoBuildDefaultRoadBlock(TileIndex tile, const AiDefaultBlockData *p, uint32 flag)
-{
- CommandCost ret;
- CommandCost total_cost(EXPENSES_CONSTRUCTION);
- Town *t = NULL;
- int rating = 0;
- int roadflag = 0;
-
- for (;p->mode != 4;p++) {
- TileIndex c = TILE_MASK(tile + ToTileIndexDiff(p->tileoffs));
-
- _cleared_town = NULL;
-
- if (p->mode == 2) {
- if (IsNormalRoadTile(c) &&
- (GetRoadBits(c, ROADTYPE_ROAD) & p->attr) != 0) {
- roadflag |= 2;
-
- // all bits are already built?
- if ((GetRoadBits(c, ROADTYPE_ROAD) & p->attr) == p->attr) continue;
- }
-
- ret = DoCommand(c, p->attr, 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD);
- if (CmdFailed(ret)) return CMD_ERROR;
- total_cost.AddCost(ret);
-
- continue;
- }
-
- if (p->mode == 0) {
- // Depot
- ret = DoCommand(c, p->attr, 0, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_ROAD_DEPOT);
- goto clear_town_stuff;
- } else if (p->mode == 1) {
- if (_want_road_truck_station) {
- // Truck station
- ret = DoCommand(c, p->attr, ROADTYPES_ROAD << 2 | ROADSTOP_TRUCK | INVALID_STATION << 16, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_ROAD_STOP);
- } else {
- // Bus station
- ret = DoCommand(c, p->attr, ROADTYPES_ROAD << 2 | ROADSTOP_BUS | INVALID_STATION << 16, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_BUILD_ROAD_STOP);
- }
-clear_town_stuff:;
-
- if (CmdFailed(ret)) return CMD_ERROR;
- total_cost.AddCost(ret);
-
- if (_cleared_town != NULL) {
- if (t != NULL && t != _cleared_town) return CMD_ERROR;
- t = _cleared_town;
- rating += _cleared_town_rating;
- }
- } else if (p->mode == 3) {
- if (flag & DC_EXEC) continue;
-
- if (GetTileSlope(c, NULL) != SLOPE_FLAT) return CMD_ERROR;
-
- if (!IsNormalRoadTile(c)) {
- ret = DoCommand(c, 0, 0, flag | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, CMD_LANDSCAPE_CLEAR);
- if (CmdFailed(ret)) return CMD_ERROR;
- }
-
- }
- }
-
- if (!_want_road_truck_station && !(roadflag & 2)) return CMD_ERROR;
-
- if (!(flag & DC_EXEC)) {
- if (t != NULL && rating > t->ratings[_current_company]) return CMD_ERROR;
- }
- return total_cost;
-}
-
-// Make sure the blocks are not too close to each other
-static bool AiCheckBlockDistances(Company *c, TileIndex tile)
-{
- const AiBuildRec *aib = &_companies_ai[c->index].src;
- uint num = _companies_ai[c->index].num_build_rec;
-
- do {
- if (aib->cur_building_rule != 255) {
- if (DistanceManhattan(aib->use_tile, tile) < 9) return false;
- }
- } while (++aib, --num);
-
- return true;
-}
-
-
-static void AiStateBuildDefaultRoadBlocks(Company *c)
-{
- uint i;
- int j;
- AiBuildRec *aib;
- int rule;
- CommandCost cost;
-
- // time out?
- if (++_companies_ai[c->index].timeout_counter == 1388) {
- _companies_ai[c->index].state = AIS_DELETE_RAIL_BLOCKS;
- return;
- }
-
- // do the following 8 times
- for (i = 0; i != 8; i++) {
- // check if we can build the default track
- aib = &_companies_ai[c->index].src;
- j = _companies_ai[c->index].num_build_rec;
- do {
- // this item has already been built?
- if (aib->cur_building_rule != 255) continue;
-
- // adjust the coordinate randomly,
- // to make sure that we find a position.
- aib->use_tile = AdjustTileCoordRandomly(aib->spec_tile, aib->rand_rng);
-
- // check if the road can be built there.
- rule = AiFindBestDefaultRoadBlock(
- aib->use_tile, aib->direction, aib->cargo, &cost
- );
-
- if (rule == -1) {
- // cannot build, terraform after a while
- if (_companies_ai[c->index].state_counter >= 600) {
- AiDoTerraformLand(aib->use_tile, (DiagDirection)(Random() & 3), 3, (int8)_companies_ai[c->index].state_mode);
- }
- // also try the other terraform direction
- if (++_companies_ai[c->index].state_counter >= 1000) {
- _companies_ai[c->index].state_counter = 0;
- _companies_ai[c->index].state_mode = -_companies_ai[c->index].state_mode;
- }
- } else if (CheckCompanyHasMoney(cost) && AiCheckBlockDistances(c, aib->use_tile)) {
- CommandCost r;
-
- // company has money, build it.
- aib->cur_building_rule = rule;
-
- r = AiDoBuildDefaultRoadBlock(
- aib->use_tile,
- _road_default_block_data[rule]->data,
- DC_EXEC | DC_NO_TOWN_RATING
- );
- assert(CmdSucceeded(r));
- }
- } while (++aib, --j);
- }
-
- // check if we're done with all of them
- aib = &_companies_ai[c->index].src;
- j = _companies_ai[c->index].num_build_rec;
- do {
- if (aib->cur_building_rule == 255) return;
- } while (++aib, --j);
-
- // yep, all are done. switch state to the rail building state.
- _companies_ai[c->index].state = AIS_BUILD_ROAD;
- _companies_ai[c->index].state_mode = 255;
-}
-
-struct AiRoadFinder {
- TileIndex final_tile;
- DiagDirection final_dir;
- byte depth;
- byte recursive_mode;
- DiagDirection cur_best_dir;
- DiagDirection best_dir;
- byte cur_best_depth;
- byte best_depth;
- uint cur_best_dist;
- const byte *best_ptr;
- uint best_dist;
- TileIndex cur_best_tile, best_tile;
- TileIndex bridge_end_tile;
- Company *company;
-};
-
-struct AiRoadEnum {
- TileIndex dest;
- TileIndex best_tile;
- Trackdir best_track;
- uint best_dist;
-};
-
-static void AiBuildRoadRecursive(AiRoadFinder *arf, TileIndex tile, DiagDirection dir);
-
-static bool AiCheckRoadPathBetter(AiRoadFinder *arf, const byte *p)
-{
- bool better = false;
-
- if (arf->recursive_mode < 1) {
- // Mode is 0. This means destination has not been found yet.
- // If the found path is shorter than the current one, remember it.
- if (arf->cur_best_dist < arf->best_dist ||
- (arf->cur_best_dist == arf->best_dist && arf->cur_best_depth < arf->best_depth)) {
- arf->best_depth = arf->cur_best_depth;
- arf->best_dist = arf->cur_best_dist;
- arf->best_dir = arf->cur_best_dir;
- arf->best_ptr = p;
- arf->best_tile = arf->cur_best_tile;
- better = true;
- }
- } else if (arf->recursive_mode > 1) {
- // Mode is 2.
- if (arf->best_dist != 0 || arf->cur_best_depth < arf->best_depth) {
- arf->best_depth = arf->cur_best_depth;
- arf->best_dist = 0;
- arf->best_ptr = p;
- arf->best_tile = 0;
- better = true;
- }
- }
- arf->recursive_mode = 0;
- arf->cur_best_dist = UINT_MAX;
- arf->cur_best_depth = 0xff;
-
- return better;
-}
-
-
-static bool AiEnumFollowRoad(TileIndex tile, AiRoadEnum *a, Trackdir track, uint length)
-{
- uint dist = DistanceManhattan(tile, a->dest);
-
- if (dist <= a->best_dist) {
- TileIndex tile2 = TILE_MASK(tile + TileOffsByDiagDir(TrackdirToExitdir(track)));
-
- if (IsNormalRoadTile(tile2)) {
- a->best_dist = dist;
- a->best_tile = tile;
- a->best_track = track;
- }
- }
-
- return false;
-}
-
-static bool AiCheckRoadFinished(Company *c)
-{
- AiRoadEnum are;
- TileIndex tile;
- DiagDirection dir = _companies_ai[c->index].cur_dir_a;
-
- are.dest = _companies_ai[c->index].cur_tile_b;
- tile = TILE_MASK(_companies_ai[c->index].cur_tile_a + TileOffsByDiagDir(dir));
-
- if (IsRoadStopTile(tile) || IsRoadDepotTile(tile)) return false;
- TrackdirBits bits = TrackStatusToTrackdirBits(GetTileTrackStatus(tile, TRANSPORT_ROAD, ROADTYPES_ROAD)) & DiagdirReachesTrackdirs(dir);
- if (bits == TRACKDIR_BIT_NONE) return false;
-
- are.best_dist = UINT_MAX;
-
- while (bits != TRACKDIR_BIT_NONE) {
- Trackdir trackdir = RemoveFirstTrackdir(&bits);
- FollowTrack(tile, PATHFIND_FLAGS_DISABLE_TILE_HASH, TRANSPORT_ROAD, ROADTYPES_ROAD, TrackdirToExitdir(trackdir), (TPFEnumProc*)AiEnumFollowRoad, NULL, &are);
- }
-
- if (DistanceManhattan(tile, are.dest) <= are.best_dist) return false;
-
- if (are.best_dist == 0) return true;
-
- _companies_ai[c->index].cur_tile_a = are.best_tile;
- _companies_ai[c->index].cur_dir_a = TrackdirToExitdir(are.best_track);
- return false;
-}
-
-
-static bool AiBuildRoadHelper(TileIndex tile, uint32 flags, int type)
-{
- static const RoadBits _road_bits[] = {
- ROAD_X,
- ROAD_Y,
- ROAD_NW | ROAD_NE,
- ROAD_SW | ROAD_SE,
- ROAD_NW | ROAD_SW,
- ROAD_SE | ROAD_NE
- };
- return CmdSucceeded(DoCommand(tile, _road_bits[type], 0, flags, CMD_BUILD_ROAD));
-}
-
-static inline void AiCheckBuildRoadBridgeHere(AiRoadFinder *arf, TileIndex tile, const byte *p)
-{
- Slope tileh;
- uint z;
- bool flag;
-
- DiagDirection dir2 = (DiagDirection)(p[0] & 3);
-
- tileh = GetTileSlope(tile, &z);
- if (tileh == InclinedSlope(ReverseDiagDir(dir2)) || (tileh == SLOPE_FLAT && z != 0)) {
- TileIndex tile_new = tile;
-
- // Allow bridges directly over bottom tiles
- flag = z == 0;
- for (;;) {
- TileType type;
-
- if ((TileIndexDiff)tile_new < -TileOffsByDiagDir(dir2)) return; // Wraping around map, no bridge possible!
- tile_new = TILE_MASK(tile_new + TileOffsByDiagDir(dir2));
- type = GetTileType(tile_new);
-
- if (type == MP_CLEAR || type == MP_TREES || GetTileSlope(tile_new, NULL) != SLOPE_FLAT) {
- // Allow a bridge if either we have a tile that's water, rail or street,
- // or if we found an up tile.
- if (!flag) return;
- break;
- }
- if (type != MP_WATER && type != MP_RAILWAY && type != MP_ROAD) return;
- flag = true;
- }
-
- // Is building a (rail)bridge possible at this place (type doesn't matter)?
- if (CmdFailed(DoCommand(tile_new, tile, ROADTYPES_ROAD << 8 | TRANSPORT_ROAD << 15, DC_AUTO, CMD_BUILD_BRIDGE)))
- return;
- AiBuildRoadRecursive(arf, tile_new, dir2);
-
- // At the bottom depth, check if the new path is better than the old one.
- if (arf->depth == 1) {
- if (AiCheckRoadPathBetter(arf, p)) arf->bridge_end_tile = tile_new;
- }
- }
-}
-
-static inline void AiCheckBuildRoadTunnelHere(AiRoadFinder *arf, TileIndex tile, const byte *p)
-{
- uint z;
-
- if (GetTileSlope(tile, &z) == InclinedSlope((DiagDirection)(p[0] & 3)) && z != 0) {
- CommandCost cost = DoCommand(tile, 0x200, 0, DC_AUTO, CMD_BUILD_TUNNEL);
-
- if (CmdSucceeded(cost) && cost.GetCost() <= (arf->company->money >> 4)) {
- AiBuildRoadRecursive(arf, _build_tunnel_endtile, (DiagDirection)(p[0] & 3));
- if (arf->depth == 1) AiCheckRoadPathBetter(arf, p);
- }
- }
-}
-
-
-
-static void AiBuildRoadRecursive(AiRoadFinder *arf, TileIndex tile, DiagDirection dir)
-{
- const byte *p;
-
- tile = TILE_MASK(tile + TileOffsByDiagDir(dir));
-
- // Reached destination?
- if (tile == arf->final_tile) {
- if (ReverseDiagDir(arf->final_dir) == dir) {
- arf->recursive_mode = 2;
- arf->cur_best_depth = arf->depth;
- }
- return;
- }
-
- // Depth too deep?
- if (arf->depth >= 4) {
- uint dist = DistanceMaxPlusManhattan(tile, arf->final_tile);
- if (dist < arf->cur_best_dist) {
- // Store the tile that is closest to the final position.
- arf->cur_best_dist = dist;
- arf->cur_best_tile = tile;
- arf->cur_best_dir = dir;
- arf->cur_best_depth = arf->depth;
- }
- return;
- }
-
- // Increase recursion depth
- arf->depth++;
-
- // Grab pointer to list of stuff that is possible to build
- p = _ai_table_15[dir];
-
- // Try to build a single rail in all directions.
- if (GetTileZ(tile) == 0) {
- p += 6;
- } else {
- do {
- // Make sure that a road can be built here.
- if (AiBuildRoadHelper(tile, DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, p[0])) {
- AiBuildRoadRecursive(arf, tile, (DiagDirection)p[1]);
- }
-
- // At the bottom depth?
- if (arf->depth == 1) AiCheckRoadPathBetter(arf, p);
-
- p += 2;
- } while (!(p[0] & 0x80));
- }
-
- AiCheckBuildRoadBridgeHere(arf, tile, p);
- AiCheckBuildRoadTunnelHere(arf, tile, p + 1);
-
- arf->depth--;
-}
-
-
-static void AiBuildRoadConstruct(Company *c)
-{
- AiRoadFinder arf;
- int i;
- TileIndex tile;
-
- // Reached destination?
- if (AiCheckRoadFinished(c)) {
- _companies_ai[c->index].state_mode = 255;
- return;
- }
-
- // Setup recursive finder and call it.
- arf.company = c;
- arf.final_tile = _companies_ai[c->index].cur_tile_b;
- arf.final_dir = _companies_ai[c->index].cur_dir_b;
- arf.depth = 0;
- arf.recursive_mode = 0;
- arf.best_ptr = NULL;
- arf.cur_best_dist = UINT_MAX;
- arf.cur_best_depth = 0xff;
- arf.best_dist = UINT_MAX;
- arf.best_depth = 0xff;
- arf.cur_best_tile = 0;
- arf.best_tile = 0;
- AiBuildRoadRecursive(&arf, _companies_ai[c->index].cur_tile_a, _companies_ai[c->index].cur_dir_a);
-
- // Reached destination?
- if (arf.recursive_mode == 2 && arf.cur_best_depth == 0) {
- _companies_ai[c->index].state_mode = 255;
- return;
- }
-
- // Didn't find anything to build?
- if (arf.best_ptr == NULL) {
- // Terraform some
-do_some_terraform:
- for (i = 0; i != 5; i++)
- AiDoTerraformLand(_companies_ai[c->index].cur_tile_a, _companies_ai[c->index].cur_dir_a, 3, 0);
-
- if (++_companies_ai[c->index].state_counter == 21) {
- _companies_ai[c->index].state_mode = 1;
-
- _companies_ai[c->index].cur_tile_a = TILE_MASK(_companies_ai[c->index].cur_tile_a + TileOffsByDiagDir(_companies_ai[c->index].cur_dir_a));
- _companies_ai[c->index].cur_dir_a = ReverseDiagDir(_companies_ai[c->index].cur_dir_a);
- _companies_ai[c->index].state_counter = 0;
- }
- return;
- }
-
- tile = TILE_MASK(_companies_ai[c->index].cur_tile_a + TileOffsByDiagDir(_companies_ai[c->index].cur_dir_a));
-
- if (arf.best_ptr[0] & 0x80) {
- TileIndex t1 = tile;
- TileIndex t2 = arf.bridge_end_tile;
-
- int32 bridge_len = GetTunnelBridgeLength(t1, t2);
-
- Axis axis = (TileX(t1) == TileX(t2) ? AXIS_Y : AXIS_X);
-
- /* try to build a long road instead of bridge - CMD_BUILD_LONG_ROAD has to fail if it couldn't build at least one piece! */
- CommandCost cost = DoCommand(t2, t1, (t2 < t1 ? 1 : 2) | (axis << 2) | (ROADTYPE_ROAD << 3), DC_AUTO | DC_NO_WATER, CMD_BUILD_LONG_ROAD);
-
- if (CmdSucceeded(cost) && cost.GetCost() <= c->money) {
- DoCommand(t2, t1, (t2 < t1 ? 1 : 2) | (axis << 2) | (ROADTYPE_ROAD << 3), DC_AUTO | DC_EXEC | DC_NO_WATER, CMD_BUILD_LONG_ROAD);
- } else {
- int i;
-
- /* Figure out what (road)bridge type to build
- * start with best bridge, then go down to worse and worse bridges
- * unnecessary to check for worse bridge (i=0), since AI will always build that */
- for (i = MAX_BRIDGES - 1; i != 0; i--) {
- if (CheckBridge_Stuff(i, bridge_len)) {
- CommandCost cost = DoCommand(t1, t2, i | ROADTYPES_ROAD << 8 | TRANSPORT_ROAD << 15, DC_AUTO, CMD_BUILD_BRIDGE);
- if (CmdSucceeded(cost) && cost.GetCost() < (c->money >> 1) && cost.GetCost() < ((c->money + _economy.max_loan - c->current_loan) >> 5)) break;
- }
- }
-
- /* Build it */
- DoCommand(t1, t2, i | ROADTYPES_ROAD << 8 | TRANSPORT_ROAD << 15, DC_AUTO | DC_EXEC, CMD_BUILD_BRIDGE);
- }
-
- _companies_ai[c->index].cur_tile_a = t2;
- _companies_ai[c->index].state_counter = 0;
- } else if (arf.best_ptr[0] & 0x40) {
- // tunnel
- DoCommand(tile, 0x200, 0, DC_AUTO | DC_EXEC, CMD_BUILD_TUNNEL);
- _companies_ai[c->index].cur_tile_a = _build_tunnel_endtile;
- _companies_ai[c->index].state_counter = 0;
- } else {
- // road
- if (!AiBuildRoadHelper(tile, DC_EXEC | DC_AUTO | DC_NO_WATER | DC_AI_BUILDING, arf.best_ptr[0]))
- goto do_some_terraform;
-
- _companies_ai[c->index].cur_dir_a = (DiagDirection)(arf.best_ptr[1] & 3);
- _companies_ai[c->index].cur_tile_a = tile;
- _companies_ai[c->index].state_counter = 0;
- }
-
- if (arf.best_tile != 0) {
- for (i = 0; i != 2; i++)
- AiDoTerraformLand(arf.best_tile, arf.best_dir, 3, 0);
- }
-}
-
-
-static void AiBuildRoad(Company *c)
-{
- if (_companies_ai[c->index].state_mode < 1) {
- // Construct mode, build new road.
- AiBuildRoadConstruct(c);
- } else if (_companies_ai[c->index].state_mode == 1) {
- // Destruct mode, not implemented for roads.
- _companies_ai[c->index].state_mode = 2;
- _companies_ai[c->index].state_counter = 0;
- } else if (_companies_ai[c->index].state_mode == 2) {
- uint i;
-
- // Terraform some and then try building again.
- for (i = 0; i != 4; i++) {
- AiDoTerraformLand(_companies_ai[c->index].cur_tile_a, _companies_ai[c->index].cur_dir_a, 3, 0);
- }
-
- if (++_companies_ai[c->index].state_counter == 4) {
- _companies_ai[c->index].state_counter = 0;
- _companies_ai[c->index].state_mode = 0;
- }
- }
-}
-
-static TileIndex AiGetRoadBlockEdge(byte rule, TileIndex tile, DiagDirection *dir)
-{
- const AiDefaultBlockData *p = _road_default_block_data[rule]->data;
- while (p->mode != 1) p++;
- *dir = p->attr;
- return TILE_ADD(tile, ToTileIndexDiff(p->tileoffs));
-}
-
-
-static void AiStateBuildRoad(Company *c)
-{
- int num;
- AiBuildRec *aib;
- byte cmd;
- TileIndex tile;
- DiagDirection dir;
-
- // time out?
- if (++_companies_ai[c->index].timeout_counter == 1388) {
- _companies_ai[c->index].state = AIS_DELETE_ROAD_BLOCKS;
- return;
- }
-
- // Currently building a road between two points?
- if (_companies_ai[c->index].state_mode != 255) {
- AiBuildRoad(c);
-
- // Alternate between edges
- Swap(_companies_ai[c->index].start_tile_a, _companies_ai[c->index].start_tile_b);
- Swap(_companies_ai[c->index].cur_tile_a, _companies_ai[c->index].cur_tile_b);
- Swap(_companies_ai[c->index].start_dir_a, _companies_ai[c->index].start_dir_b);
- Swap(_companies_ai[c->index].cur_dir_a, _companies_ai[c->index].cur_dir_b);
-
- return;
- }
-
- // Now, find two new points to build between
- num = _companies_ai[c->index].num_build_rec;
- aib = &_companies_ai[c->index].src;
-
- for (;;) {
- cmd = aib->buildcmd_a;
- aib->buildcmd_a = 255;
- if (cmd != 255) break;
-
- aib++;
- if (--num == 0) {
- _companies_ai[c->index].state = AIS_BUILD_ROAD_VEHICLES;
- return;
- }
- }
-
- // Find first edge to build from.
- tile = AiGetRoadBlockEdge(aib->cur_building_rule, aib->use_tile, &dir);
- _companies_ai[c->index].start_tile_a = tile;
- _companies_ai[c->index].cur_tile_a = tile;
- _companies_ai[c->index].start_dir_a = dir;
- _companies_ai[c->index].cur_dir_a = dir;
-
- // Find second edge to build to
- aib = (&_companies_ai[c->index].src) + (cmd & 0xF);
- tile = AiGetRoadBlockEdge(aib->cur_building_rule, aib->use_tile, &dir);
- _companies_ai[c->index].start_tile_b = tile;
- _companies_ai[c->index].cur_tile_b = tile;
- _companies_ai[c->index].start_dir_b = dir;
- _companies_ai[c->index].cur_dir_b = dir;
-
- // And setup state.
- _companies_ai[c->index].state_mode = 2;
- _companies_ai[c->index].state_counter = 0;
- _companies_ai[c->index].banned_tile_count = 0;
-}
-
-static StationID AiGetStationIdFromRoadBlock(TileIndex tile, int id)
-{
- const AiDefaultBlockData *p = _road_default_block_data[id]->data;
- while (p->mode != 1) p++;
- return GetStationIndex(TILE_ADD(tile, ToTileIndexDiff(p->tileoffs)));
-}
-
-static void AiStateBuildRoadVehicles(Company *c)
-{
- const AiDefaultBlockData *ptr;
- TileIndex tile;
- VehicleID loco_id;
- EngineID veh;
- uint i;
-
- ptr = _road_default_block_data[_companies_ai[c->index].src.cur_building_rule]->data;
- for (; ptr->mode != 0; ptr++) {}
- tile = TILE_ADD(_companies_ai[c->index].src.use_tile, ToTileIndexDiff(ptr->tileoffs));
-
- veh = AiChooseRoadVehToBuild(_companies_ai[c->index].cargo_type, c->money, tile);
- if (veh == INVALID_ENGINE) {
- _companies_ai[c->index].state = AIS_0;
- return;
- }
-
- if (CmdFailed(DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_ROAD_VEH))) return;
-
- loco_id = _new_vehicle_id;
-
- if (GetVehicle(loco_id)->cargo_type != _companies_ai[c->index].cargo_type) {
- /* Cargo type doesn't match, so refit it */
- if (CmdFailed(DoCommand(tile, loco_id, _companies_ai[c->index].cargo_type, DC_EXEC, CMD_REFIT_ROAD_VEH))) {
- /* Refit failed... sell the vehicle */
- DoCommand(tile, loco_id, 0, DC_EXEC, CMD_SELL_ROAD_VEH);
- return;
- }
- }
-
- for (i = 0; _companies_ai[c->index].order_list_blocks[i] != 0xFF; i++) {
- const AiBuildRec *aib = &_companies_ai[c->index].src + _companies_ai[c->index].order_list_blocks[i];
- bool is_pass = (
- _companies_ai[c->index].cargo_type == CT_PASSENGERS ||
- _companies_ai[c->index].cargo_type == CT_MAIL ||
- (_settings_game.game_creation.landscape == LT_TEMPERATE && _companies_ai[c->index].cargo_type == CT_VALUABLES)
- );
- Order order;
-
- order.MakeGoToStation(AiGetStationIdFromRoadBlock(aib->use_tile, aib->cur_building_rule));
-
- if (!is_pass && i == 1) order.SetUnloadType(OUFB_UNLOAD);
- if (_companies_ai[c->index].num_want_fullload != 0 && (is_pass || i == 0))
- order.SetLoadType(OLFB_FULL_LOAD);
-
- DoCommand(0, loco_id + (i << 16), order.Pack(), DC_EXEC, CMD_INSERT_ORDER);
- }
-
- DoCommand(0, loco_id, 0, DC_EXEC, CMD_START_STOP_VEHICLE);
- DoCommand(0, loco_id, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT);
-
- if (_companies_ai[c->index].num_want_fullload != 0) _companies_ai[c->index].num_want_fullload--;
- if (--_companies_ai[c->index].num_loco_to_build == 0) _companies_ai[c->index].state = AIS_0;
-}
-
-static void AiStateDeleteRoadBlocks(Company *c)
-{
- const AiBuildRec *aib = &_companies_ai[c->index].src;
- uint num = _companies_ai[c->index].num_build_rec;
-
- do {
- const AiDefaultBlockData *b;
-
- if (aib->cur_building_rule == 255) continue;
- for (b = _road_default_block_data[aib->cur_building_rule]->data; b->mode != 4; b++) {
- if (b->mode > 1) continue;
- DoCommand(TILE_ADD(aib->use_tile, ToTileIndexDiff(b->tileoffs)), 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
- }
- } while (++aib, --num);
-
- _companies_ai[c->index].state = AIS_0;
-}
-
-
-static void AiStateAirportStuff(Company *c)
-{
- const Station *st;
- int i;
- AiBuildRec *aib;
- byte rule;
-
- // Here we look for an airport we could use instead of building a new
- // one. If we find such an aiport for any waypoint,
- // AiStateBuildDefaultAirportBlocks() will kindly skip that one when
- // building the waypoints.
-
- i = 0;
- do {
- // We do this all twice - once for the source (town in the case
- // of oilrig route) and then for the destination (oilrig in the
- // case of oilrig route).
- aib = &_companies_ai[c->index].src + i;
-
- FOR_ALL_STATIONS(st) {
- // Is this an airport?
- if (!(st->facilities & FACIL_AIRPORT)) continue;
-
- // Do we own the airport? (Oilrigs aren't owned, though.)
- if (st->owner != OWNER_NONE && st->owner != _current_company) continue;
-
- AirportFTAClass::Flags flags = st->Airport()->flags;
-
- /* if airport doesn't accept our kind of plane, dismiss it */
- if (!(flags & (_companies_ai[c->index].build_kind == 1 ? AirportFTAClass::HELICOPTERS : AirportFTAClass::AIRPLANES))) {
- continue;
- }
-
- // Dismiss airports too far away.
- if (DistanceMax(st->airport_tile, aib->spec_tile) > aib->rand_rng)
- continue;
-
- // It's ideal airport, let's take it!
-
- /* XXX: This part is utterly broken - rule should
- * contain number of the rule appropriate for the
- * airport type (country, town, ...), see
- * _airport_default_block_data (rule is just an index
- * in this array). But the only difference between the
- * currently existing two rules (rule 0 - town and rule
- * 1 - country) is the attr field which is used only
- * when building new airports - and that's irrelevant
- * for us. So using just about any rule will suffice
- * here for now (some of the new airport types would be
- * broken because they will probably need different
- * tileoff values etc), no matter that
- * IsHangarTile() makes no sense. --pasky */
- if (!(flags & AirportFTAClass::AIRPLANES)) {
- /* Heliports should have maybe own rulesets but
- * OTOH we don't want AI to pick them up when
- * looking for a suitable airport type to build.
- * So any of rules 0 or 1 would do for now. The
- * original rule number was 2 but that's a bug
- * because we have no such rule. */
- rule = 1;
- } else {
- rule = IsHangarTile(st->airport_tile);
- }
-
- aib->cur_building_rule = rule;
- aib->use_tile = st->airport_tile;
- break;
- }
- } while (++i != _companies_ai[c->index].num_build_rec);
-
- _companies_ai[c->index].state = AIS_BUILD_DEFAULT_AIRPORT_BLOCKS;
- _companies_ai[c->index].state_mode = 255;
- _companies_ai[c->index].state_counter = 0;
-}
-
-static CommandCost AiDoBuildDefaultAirportBlock(TileIndex tile, const AiDefaultBlockData *p, uint32 flag)
-{
- uint32 avail_airports = GetValidAirports();
- CommandCost ret, total_cost(EXPENSES_CONSTRUCTION);
-
- for (; p->mode == 0; p++) {
- if (!HasBit(avail_airports, p->attr)) return CMD_ERROR;
- ret = DoCommand(TILE_MASK(tile + ToTileIndexDiff(p->tileoffs)), p->attr, INVALID_STATION << 16, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_AIRPORT);
- if (CmdFailed(ret)) return CMD_ERROR;
- total_cost.AddCost(ret);
- }
-
- return total_cost;
-}
-
-static bool AiCheckAirportResources(TileIndex tile, const AiDefaultBlockData *p, byte cargo)
-{
- uint values[NUM_CARGO];
-
- for (; p->mode == 0; p++) {
- TileIndex tile2 = TILE_ADD(tile, ToTileIndexDiff(p->tileoffs));
- const AirportFTAClass *airport = GetAirport(p->attr);
- uint w = airport->size_x;
- uint h = airport->size_y;
- uint rad = _settings_game.station.modified_catchment ? airport->catchment : (uint)CA_UNMODIFIED;
-
- if (cargo & 0x80) {
- GetProductionAroundTiles(values, tile2, w, h, rad);
- return values[cargo & 0x7F] != 0;
- } else {
- GetAcceptanceAroundTiles(values, tile2, w, h, rad);
- return values[cargo] >= 8;
- }
- }
- return true;
-}
-
-static int AiFindBestDefaultAirportBlock(TileIndex tile, byte cargo, byte heli, CommandCost *cost)
-{
- const AiDefaultBlockData *p;
-
- bool no_small = false;
-
- if (!heli) {
- /* do not build small airport if we have large available and we are not building heli route */
- uint valid = GetValidAirports();
- for (uint i = 0; (p = _airport_default_block_data[i]) != NULL; i++) {
- uint flags = GetAirport(p->attr)->flags;
- if (HasBit(valid, p->attr) && (flags & AirportFTAClass::AIRPLANES) && !(flags & AirportFTAClass::SHORT_STRIP)) {
- no_small = true;
- break;
- }
- }
- }
-
- for (uint i = 0; (p = _airport_default_block_data[i]) != NULL; i++) {
- uint flags = GetAirport(p->attr)->flags;
- /* If we are doing a helicopter service, avoid building airports where they can't land */
- if (heli && !(flags & AirportFTAClass::HELICOPTERS)) continue;
- /* Similiar with aircraft ... */
- if (!heli && !(flags & AirportFTAClass::AIRPLANES)) continue;
- /* Do not build small airport if we prefer large */
- if (no_small && (flags & AirportFTAClass::SHORT_STRIP)) continue;
-
- *cost = AiDoBuildDefaultAirportBlock(tile, p, 0);
- if (CmdSucceeded(*cost) && AiCheckAirportResources(tile, p, cargo))
- return i;
- }
- return -1;
-}
-
-static void AiStateBuildDefaultAirportBlocks(Company *c)
-{
- int i, j;
- AiBuildRec *aib;
- int rule;
- CommandCost cost;
-
- // time out?
- if (++_companies_ai[c->index].timeout_counter == 1388) {
- _companies_ai[c->index].state = AIS_0;
- return;
- }
-
- // do the following 8 times
- i = 8;
- do {
- // check if we can build the default
- aib = &_companies_ai[c->index].src;
- j = _companies_ai[c->index].num_build_rec;
- do {
- // this item has already been built?
- if (aib->cur_building_rule != 255) continue;
-
- // adjust the coordinate randomly,
- // to make sure that we find a position.
- aib->use_tile = AdjustTileCoordRandomly(aib->spec_tile, aib->rand_rng);
-
- // check if the aircraft stuff can be built there.
- rule = AiFindBestDefaultAirportBlock(aib->use_tile, aib->cargo, _companies_ai[c->index].build_kind, &cost);
-
-// SetRedErrorSquare(aib->use_tile);
-
- if (rule == -1) {
- // cannot build, terraform after a while
- if (_companies_ai[c->index].state_counter >= 600) {
- AiDoTerraformLand(aib->use_tile, (DiagDirection)(Random() & 3), 3, (int8)_companies_ai[c->index].state_mode);
- }
- // also try the other terraform direction
- if (++_companies_ai[c->index].state_counter >= 1000) {
- _companies_ai[c->index].state_counter = 0;
- _companies_ai[c->index].state_mode = -_companies_ai[c->index].state_mode;
- }
- } else if (CheckCompanyHasMoney(cost) && AiCheckBlockDistances(c, aib->use_tile)) {
- // company has money, build it.
- CommandCost r;
-
- aib->cur_building_rule = rule;
-
- r = AiDoBuildDefaultAirportBlock(
- aib->use_tile,
- _airport_default_block_data[rule],
- DC_EXEC | DC_NO_TOWN_RATING
- );
- assert(CmdSucceeded(r));
- }
- } while (++aib, --j);
- } while (--i);
-
- // check if we're done with all of them
- aib = &_companies_ai[c->index].src;
- j = _companies_ai[c->index].num_build_rec;
- do {
- if (aib->cur_building_rule == 255) return;
- } while (++aib, --j);
-
- // yep, all are done. switch state.
- _companies_ai[c->index].state = AIS_BUILD_AIRCRAFT_VEHICLES;
-}
-
-static StationID AiGetStationIdFromAircraftBlock(TileIndex tile, int id)
-{
- const AiDefaultBlockData *p = _airport_default_block_data[id];
- while (p->mode != 1) p++;
- return GetStationIndex(TILE_ADD(tile, ToTileIndexDiff(p->tileoffs)));
-}
-
-static void AiStateBuildAircraftVehicles(Company *c)
-{
- const AiDefaultBlockData *ptr;
- TileIndex tile;
- EngineID veh;
- int i;
- VehicleID loco_id;
-
- ptr = _airport_default_block_data[_companies_ai[c->index].src.cur_building_rule];
- for (; ptr->mode != 0; ptr++) {}
-
- tile = TILE_ADD(_companies_ai[c->index].src.use_tile, ToTileIndexDiff(ptr->tileoffs));
-
- /* determine forbidden aircraft bits */
- byte forbidden = 0;
- for (i = 0; _companies_ai[c->index].order_list_blocks[i] != 0xFF; i++) {
- const AiBuildRec *aib = (&_companies_ai[c->index].src) + _companies_ai[c->index].order_list_blocks[i];
- const Station *st = GetStationByTile(aib->use_tile);
-
- if (st == NULL || !(st->facilities & FACIL_AIRPORT)) continue;
-
- AirportFTAClass::Flags flags = st->Airport()->flags;
- if (!(flags & AirportFTAClass::AIRPLANES)) forbidden |= AIR_CTOL | AIR_FAST; // no planes for heliports / oil rigs
- if (flags & AirportFTAClass::SHORT_STRIP) forbidden |= AIR_FAST; // no fast planes for small airports
- }
-
- veh = AiChooseAircraftToBuild(c->money, forbidden);
- if (veh == INVALID_ENGINE) return;
- if (GetStationByTile(tile)->Airport()->nof_depots == 0) return;
-
- /* XXX - Have the AI pick the hangar terminal in an airport. Eg get airport-type
- * and offset to the FIRST depot because the AI picks the st->xy tile */
- tile += ToTileIndexDiff(GetStationByTile(tile)->Airport()->airport_depots[0]);
- if (CmdFailed(DoCommand(tile, veh, 0, DC_EXEC, CMD_BUILD_AIRCRAFT))) return;
- loco_id = _new_vehicle_id;
-
- for (i = 0; _companies_ai[c->index].order_list_blocks[i] != 0xFF; i++) {
- AiBuildRec *aib = (&_companies_ai[c->index].src) + _companies_ai[c->index].order_list_blocks[i];
- bool is_pass = (_companies_ai[c->index].cargo_type == CT_PASSENGERS || _companies_ai[c->index].cargo_type == CT_MAIL);
- Order order;
-
- order.MakeGoToStation(AiGetStationIdFromAircraftBlock(aib->use_tile, aib->cur_building_rule));
-
- if (!is_pass && i == 1) order.SetUnloadType(OUFB_UNLOAD);
- if (_companies_ai[c->index].num_want_fullload != 0 && (is_pass || i == 0))
- order.SetLoadType(OLFB_FULL_LOAD);
-
- DoCommand(0, loco_id + (i << 16), order.Pack(), DC_EXEC, CMD_INSERT_ORDER);
- }
-
- DoCommand(0, loco_id, 0, DC_EXEC, CMD_START_STOP_VEHICLE);
-
- DoCommand(0, loco_id, _ai_service_interval, DC_EXEC, CMD_CHANGE_SERVICE_INT);
-
- if (_companies_ai[c->index].num_want_fullload != 0) _companies_ai[c->index].num_want_fullload--;
-
- if (--_companies_ai[c->index].num_loco_to_build == 0) _companies_ai[c->index].state = AIS_0;
-}
-
-static void AiStateCheckShipStuff(Company *c)
-{
- /* Ships are not implemented in this (broken) AI */
-}
-
-static void AiStateBuildDefaultShipBlocks(Company *c)
-{
- /* Ships are not implemented in this (broken) AI */
-}
-
-static void AiStateDoShipStuff(Company *c)
-{
- /* Ships are not implemented in this (broken) AI */
-}
-
-static void AiStateSellVeh(Company *c)
-{
- Vehicle *v = _companies_ai[c->index].cur_veh;
-
- if (v->owner == _current_company) {
- if (v->type == VEH_TRAIN) {
-
- if (!IsRailDepotTile(v->tile) || v->u.rail.track != TRACK_BIT_DEPOT || !(v->vehstatus & VS_STOPPED)) {
- if (!v->current_order.IsType(OT_GOTO_DEPOT))
- DoCommand(0, v->index, 0, DC_EXEC, CMD_SEND_TRAIN_TO_DEPOT);
- goto going_to_depot;
- }
-
- // Sell whole train
- DoCommand(v->tile, v->index, 1, DC_EXEC, CMD_SELL_RAIL_WAGON);
-
- } else if (v->type == VEH_ROAD) {
- if (!v->IsStoppedInDepot()) {
- if (!v->current_order.IsType(OT_GOTO_DEPOT))
- DoCommand(0, v->index, 0, DC_EXEC, CMD_SEND_ROADVEH_TO_DEPOT);
- goto going_to_depot;
- }
-
- DoCommand(0, v->index, 0, DC_EXEC, CMD_SELL_ROAD_VEH);
- } else if (v->type == VEH_AIRCRAFT) {
- if (!v->IsStoppedInDepot()) {
- if (!v->current_order.IsType(OT_GOTO_DEPOT))
- DoCommand(0, v->index, 0, DC_EXEC, CMD_SEND_AIRCRAFT_TO_HANGAR);
- goto going_to_depot;
- }
-
- DoCommand(0, v->index, 0, DC_EXEC, CMD_SELL_AIRCRAFT);
- } else if (v->type == VEH_SHIP) {
- /* Ships are not implemented in this (broken) AI */
- }
- }
-
- goto return_to_loop;
-going_to_depot:;
- if (++_companies_ai[c->index].state_counter <= 832) return;
-
- if (v->current_order.IsType(OT_GOTO_DEPOT)) {
- v->current_order.MakeDummy();
- InvalidateWindow(WC_VEHICLE_VIEW, v->index);
- }
-return_to_loop:;
- _companies_ai[c->index].state = AIS_VEH_LOOP;
-}
-
-static void AiStateRemoveStation(Company *c)
-{
- // Remove stations that aren't in use by any vehicle
- const Order *ord;
- const Station *st;
- TileIndex tile;
-
- // Go to this state when we're done.
- _companies_ai[c->index].state = AIS_1;
-
- // Get a list of all stations that are in use by a vehicle
- byte *in_use = MallocT<byte>(GetMaxStationIndex() + 1);
- memset(in_use, 0, GetMaxStationIndex() + 1);
- FOR_ALL_ORDERS(ord) {
- if (ord->IsType(OT_GOTO_STATION)) in_use[ord->GetDestination()] = 1;
- }
-
- // Go through all stations and delete those that aren't in use
- FOR_ALL_STATIONS(st) {
- if (st->owner == _current_company && !in_use[st->index] &&
- ( (st->bus_stops != NULL && (tile = st->bus_stops->xy) != INVALID_TILE) ||
- (st->truck_stops != NULL && (tile = st->truck_stops->xy) != INVALID_TILE) ||
- (tile = st->train_tile) != INVALID_TILE ||
- (tile = st->dock_tile) != INVALID_TILE ||
- (tile = st->airport_tile) != INVALID_TILE)) {
- DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
- }
- }
-
- free(in_use);
-}
-
-static void AiRemoveCompanyRailOrRoad(Company *c, TileIndex tile)
-{
- TrackBits rails;
-
- if (IsTileType(tile, MP_RAILWAY)) {
- if (!IsTileOwner(tile, _current_company)) return;
-
- if (IsPlainRailTile(tile)) {
-is_rail_crossing:;
- rails = GetRailTrackStatus(tile);
-
- if (rails == TRACK_BIT_HORZ || rails == TRACK_BIT_VERT) return;
-
- if (rails & TRACK_BIT_3WAY_NE) {
-pos_0:
- if ((GetRailTrackStatus(TILE_MASK(tile - TileDiffXY(1, 0))) & TRACK_BIT_3WAY_SW) == 0) {
- _companies_ai[c->index].cur_dir_a = DIAGDIR_NE;
- _companies_ai[c->index].cur_tile_a = tile;
- _companies_ai[c->index].state = AIS_REMOVE_SINGLE_RAIL_TILE;
- return;
- }
- }
-
- if (rails & TRACK_BIT_3WAY_SE) {
-pos_1:
- if ((GetRailTrackStatus(TILE_MASK(tile + TileDiffXY(0, 1))) & TRACK_BIT_3WAY_NW) == 0) {
- _companies_ai[c->index].cur_dir_a = DIAGDIR_SE;
- _companies_ai[c->index].cur_tile_a = tile;
- _companies_ai[c->index].state = AIS_REMOVE_SINGLE_RAIL_TILE;
- return;
- }
- }
-
- if (rails & TRACK_BIT_3WAY_SW) {
-pos_2:
- if ((GetRailTrackStatus(TILE_MASK(tile + TileDiffXY(1, 0))) & TRACK_BIT_3WAY_NE) == 0) {
- _companies_ai[c->index].cur_dir_a = DIAGDIR_SW;
- _companies_ai[c->index].cur_tile_a = tile;
- _companies_ai[c->index].state = AIS_REMOVE_SINGLE_RAIL_TILE;
- return;
- }
- }
-
- if (rails & TRACK_BIT_3WAY_NW) {
-pos_3:
- if ((GetRailTrackStatus(TILE_MASK(tile - TileDiffXY(0, 1))) & TRACK_BIT_3WAY_SE) == 0) {
- _companies_ai[c->index].cur_dir_a = DIAGDIR_NW;
- _companies_ai[c->index].cur_tile_a = tile;
- _companies_ai[c->index].state = AIS_REMOVE_SINGLE_RAIL_TILE;
- return;
- }
- }
- } else {
- static const byte _depot_bits[] = {0x19, 0x16, 0x25, 0x2A};
-
- DiagDirection dir = GetRailDepotDirection(tile);
-
- if (GetRailTrackStatus(tile + TileOffsByDiagDir(dir)) & _depot_bits[dir])
- return;
-
- DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
- }
- } else if (IsTileType(tile, MP_ROAD)) {
- if (IsLevelCrossing(tile)) goto is_rail_crossing;
-
- if (IsRoadDepot(tile)) {
- if (!IsTileOwner(tile, _current_company)) return;
-
- DiagDirection dir;
- TileIndex t;
-
- // Check if there are any stations around.
- t = tile + TileDiffXY(-1, 0);
- if (IsTileType(t, MP_STATION) && IsTileOwner(t, _current_company)) return;
-
- t = tile + TileDiffXY(1, 0);
- if (IsTileType(t, MP_STATION) && IsTileOwner(t, _current_company)) return;
-
- t = tile + TileDiffXY(0, -1);
- if (IsTileType(t, MP_STATION) && IsTileOwner(t, _current_company)) return;
-
- t = tile + TileDiffXY(0, 1);
- if (IsTileType(t, MP_STATION) && IsTileOwner(t, _current_company)) return;
-
- dir = GetRoadDepotDirection(tile);
-
- DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
- DoCommand(
- TILE_MASK(tile + TileOffsByDiagDir(dir)),
- DiagDirToRoadBits(ReverseDiagDir(dir)),
- 0,
- DC_EXEC,
- CMD_REMOVE_ROAD);
- }
- } else if (IsTileType(tile, MP_TUNNELBRIDGE)) {
- if (!IsTileOwner(tile, _current_company) ||
- !IsBridge(tile) ||
- GetTunnelBridgeTransportType(tile) != TRANSPORT_RAIL) {
- return;
- }
-
- rails = TRACK_BIT_NONE;
-
- switch (GetTunnelBridgeDirection(tile)) {
- default:
- case DIAGDIR_NE: goto pos_2;
- case DIAGDIR_SE: goto pos_3;
- case DIAGDIR_SW: goto pos_0;
- case DIAGDIR_NW: goto pos_1;
- }
- }
-}
-
-static void AiStateRemoveTrack(Company *c)
-{
- /* Was 1000 for standard 8x8 maps. */
- int num = MapSizeX() * 4;
-
- do {
- TileIndex tile = ++_companies_ai[c->index].state_counter;
-
- // Iterated all tiles?
- if (tile >= MapSize()) {
- _companies_ai[c->index].state = AIS_REMOVE_STATION;
- return;
- }
-
- // Remove company stuff in that tile
- AiRemoveCompanyRailOrRoad(c, tile);
- if (_companies_ai[c->index].state != AIS_REMOVE_TRACK) return;
- } while (--num);
-}
-
-static void AiStateRemoveSingleRailTile(Company *c)
-{
- // Remove until we can't remove more.
- if (!AiRemoveTileAndGoForward(c)) _companies_ai[c->index].state = AIS_REMOVE_TRACK;
-}
-
-static AiStateAction * const _ai_actions[] = {
- AiCase0,
- AiCase1,
- AiStateVehLoop,
- AiStateCheckReplaceVehicle,
- AiStateDoReplaceVehicle,
- AiStateWantNewRoute,
-
- AiStateBuildDefaultRailBlocks,
- AiStateBuildRail,
- AiStateBuildRailVeh,
- AiStateDeleteRailBlocks,
-
- AiStateBuildDefaultRoadBlocks,
- AiStateBuildRoad,
- AiStateBuildRoadVehicles,
- AiStateDeleteRoadBlocks,
-
- AiStateAirportStuff,
- AiStateBuildDefaultAirportBlocks,
- AiStateBuildAircraftVehicles,
-
- AiStateCheckShipStuff,
- AiStateBuildDefaultShipBlocks,
- AiStateDoShipStuff,
-
- AiStateSellVeh,
- AiStateRemoveStation,
- AiStateRemoveTrack,
-
- AiStateRemoveSingleRailTile
-};
-
-extern void ShowBuyCompanyDialog(CompanyID company);
-
-static void AiHandleTakeover(Company *c)
-{
- if (c->bankrupt_timeout != 0) {
- c->bankrupt_timeout -= 8;
- if (c->bankrupt_timeout > 0) return;
- c->bankrupt_timeout = 0;
- DeleteWindowById(WC_BUY_COMPANY, _current_company);
- if (IsLocalCompany()) {
- AskExitToGameMenu();
- return;
- }
- if (IsHumanCompany(_current_company)) return;
- }
-
- if (c->bankrupt_asked == MAX_UVALUE(CompanyMask)) return;
-
- {
- CompanyMask asked = c->bankrupt_asked;
- Company *company, *best_company = NULL;
- int32 best_val = -1;
-
- // Ask the guy with the highest performance hist.
- FOR_ALL_COMPANIES(company) {
- if (!(asked & 1) &&
- company->bankrupt_asked == 0 &&
- best_val < company->old_economy[1].performance_history) {
- best_val = company->old_economy[1].performance_history;
- best_company = company;
- }
- asked >>= 1;
- }
-
- // Asked all companies?
- if (best_val == -1) {
- c->bankrupt_asked = MAX_UVALUE(CompanyMask);
- return;
- }
-
- SetBit(c->bankrupt_asked, best_company->index);
-
- if (best_company->index == _local_company) {
- c->bankrupt_timeout = 4440;
- ShowBuyCompanyDialog(_current_company);
- return;
- }
- if (IsHumanCompany(best_company->index)) return;
-
- // Too little money for computer to buy it?
- if (best_company->money >> 1 >= c->bankrupt_value) {
- // Computer wants to buy it.
- CompanyID old_company = _current_company;
- _current_company = best_company->index;
- DoCommand(0, old_company, 0, DC_EXEC, CMD_BUY_COMPANY);
- _current_company = old_company;
- }
- }
-}
-
-static void AiAdjustLoan(const Company *c)
-{
- Money base = AiGetBasePrice(c);
-
- if (c->money > base * 1400) {
- // Decrease loan
- if (c->current_loan != 0) {
- DoCommand(0, 0, 0, DC_EXEC, CMD_DECREASE_LOAN);
- }
- } else if (c->money < base * 500) {
- // Increase loan
- if (c->current_loan < _economy.max_loan &&
- c->num_valid_stat_ent >= 2 &&
- -(c->old_economy[0].expenses + c->old_economy[1].expenses) < base * 60) {
- DoCommand(0, 0, 0, DC_EXEC, CMD_INCREASE_LOAN);
- }
- }
-}
-
-static void AiBuildCompanyHQ(Company *c)
-{
- TileIndex tile;
-
- if (c->location_of_HQ == INVALID_TILE &&
- c->last_build_coordinate != 0) {
- tile = AdjustTileCoordRandomly(c->last_build_coordinate, 8);
- DoCommand(tile, 0, 0, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_BUILD_COMPANY_HQ);
- }
-}
-
-
-void AiDoGameLoop(Company *c)
-{
- if (c->bankrupt_asked != 0) {
- AiHandleTakeover(c);
- return;
- }
-
- // Ugly hack to make sure the service interval of the AI is good, not looking
- // to the patch-setting
- // Also, it takes into account the setting if the service-interval is in days
- // or in %
- _ai_service_interval = _settings_game.vehicle.servint_ispercent ? 80 : 180;
-
- if (IsHumanCompany(_current_company)) return;
-
- AiAdjustLoan(c);
- AiBuildCompanyHQ(c);
-
-#if 0
- {
- static byte old_state = 99;
- static bool hasdots = false;
- char *_ai_state_names[] = {
- "AiCase0",
- "AiCase1",
- "AiStateVehLoop",
- "AiStateCheckReplaceVehicle",
- "AiStateDoReplaceVehicle",
- "AiStateWantNewRoute",
- "AiStateBuildDefaultRailBlocks",
- "AiStateBuildRail",
- "AiStateBuildRailVeh",
- "AiStateDeleteRailBlocks",
- "AiStateBuildDefaultRoadBlocks",
- "AiStateBuildRoad",
- "AiStateBuildRoadVehicles",
- "AiStateDeleteRoadBlocks",
- "AiStateAirportStuff",
- "AiStateBuildDefaultAirportBlocks",
- "AiStateBuildAircraftVehicles",
- "AiStateCheckShipStuff",
- "AiStateBuildDefaultShipBlocks",
- "AiStateDoShipStuff",
- "AiStateSellVeh",
- "AiStateRemoveStation",
- "AiStateRemoveTrack",
- "AiStateRemoveSingleRailTile"
- };
-
- if (_companies_ai[c->index].state != old_state) {
- if (hasdots)
- printf("\n");
- hasdots = false;
- printf("AiState: %s\n", _ai_state_names[old_state=_companies_ai[c->index].state]);
- } else {
- printf(".");
- hasdots = true;
- }
- }
-#endif
-
- _ai_actions[_companies_ai[c->index].state](c);
-}
diff --git a/src/ai/default/default.h b/src/ai/default/default.h
deleted file mode 100644
index 843cf0f17..000000000
--- a/src/ai/default/default.h
+++ /dev/null
@@ -1,69 +0,0 @@
-/* $Id$ */
-
-/** @file default.h The original AI. */
-
-#ifndef DEFAULT_H
-#define DEFAULT_H
-
-#include "../../direction_type.h"
-#include "../../vehicle_type.h"
-#include "../../rail_type.h"
-
-void AiDoGameLoop(Company *c);
-
-struct AiBuildRec {
- TileIndex spec_tile;
- TileIndex use_tile;
- byte rand_rng;
- byte cur_building_rule;
- byte unk6;
- byte unk7;
- byte buildcmd_a;
- byte buildcmd_b;
- byte direction;
- CargoID cargo;
-};
-
-struct CompanyAI {
- byte state;
- byte tick; ///< Used to determine how often to move
- uint32 state_counter; ///< Can hold tile index!
- uint16 timeout_counter;
-
- byte state_mode;
- byte banned_tile_count;
- RailTypeByte railtype_to_use;
-
- CargoID cargo_type;
- byte num_wagons;
- byte build_kind;
- byte num_build_rec;
- byte num_loco_to_build;
- byte num_want_fullload;
-
- byte route_type_mask;
-
- TileIndex start_tile_a;
- TileIndex cur_tile_a;
- DiagDirectionByte cur_dir_a;
- DiagDirectionByte start_dir_a;
-
- TileIndex start_tile_b;
- TileIndex cur_tile_b;
- DiagDirectionByte cur_dir_b;
- DiagDirectionByte start_dir_b;
-
- Vehicle *cur_veh; ///< only used by some states
-
- AiBuildRec src, dst, mid1, mid2;
-
- VehicleID wagon_list[9];
- byte order_list_blocks[20];
-
- TileIndex banned_tiles[16];
- byte banned_val[16];
-};
-
-extern CompanyAI _companies_ai[MAX_COMPANIES];
-
-#endif
diff --git a/src/ai/trolly/build.cpp b/src/ai/trolly/build.cpp
deleted file mode 100644
index 0723386f7..000000000
--- a/src/ai/trolly/build.cpp
+++ /dev/null
@@ -1,328 +0,0 @@
-/* $Id$ */
-
-/** @file build.cpp Building support for the trolly AI. */
-
-#include "../../stdafx.h"
-#include "../../openttd.h"
-#include "../../debug.h"
-#include "../../road_map.h"
-#include "../../command_func.h"
-#include "trolly.h"
-#include "../../engine_func.h"
-#include "../../engine_base.h"
-#include "../../variables.h"
-#include "../../bridge.h"
-#include "../../vehicle_func.h"
-#include "../../vehicle_base.h"
-#include "../../company_base.h"
-#include "../../company_func.h"
-#include "../ai.h"
-#include "../../tunnelbridge.h"
-
-
-// Build HQ
-// Params:
-// tile : tile where HQ is going to be build
-bool AiNew_Build_CompanyHQ(Company *c, TileIndex tile)
-{
- if (CmdFailed(AI_DoCommand(tile, 0, 0, DC_AUTO | DC_NO_WATER, CMD_BUILD_COMPANY_HQ)))
- return false;
- AI_DoCommand(tile, 0, 0, DC_EXEC | DC_AUTO | DC_NO_WATER, CMD_BUILD_COMPANY_HQ);
- return true;
-}
-
-
-// Build station
-// Params:
-// type : AI_TRAIN/AI_BUS/AI_TRUCK : indicates the type of station
-// tile : tile where station is going to be build
-// length : in case of AI_TRAIN: length of station
-// numtracks : in case of AI_TRAIN: tracks of station
-// direction : the direction of the station
-// flag : flag passed to DoCommand (normally 0 to get the cost or DC_EXEC to build it)
-CommandCost AiNew_Build_Station(Company *c, byte type, TileIndex tile, byte length, byte numtracks, byte direction, byte flag)
-{
- if (type == AI_TRAIN)
- return AI_DoCommand(tile, (direction << 4) + (numtracks << 8) + (length << 16), INVALID_STATION << 16, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_RAILROAD_STATION);
-
- if (type == AI_BUS)
- return AI_DoCommand(tile, direction, ROADTYPES_ROAD << 2 | ROADSTOP_BUS | INVALID_STATION << 16, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_STOP);
-
- return AI_DoCommand(tile, direction, ROADTYPES_ROAD << 2 | ROADSTOP_TRUCK | INVALID_STATION << 16, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_STOP);
-}
-
-
-// Builds a brdige. The second best out of the ones available for this company
-// Params:
-// tile_a : starting point
-// tile_b : end point
-// flag : flag passed to DoCommand
-CommandCost AiNew_Build_Bridge(Company *c, TileIndex tile_a, TileIndex tile_b, byte flag)
-{
- int bridge_type, bridge_len, type, type2;
-
- // Find a good bridgetype (the best money can buy)
- bridge_len = GetTunnelBridgeLength(tile_a, tile_b);
- type = type2 = 0;
- for (bridge_type = MAX_BRIDGES - 1; bridge_type >= 0; bridge_type--) {
- if (CheckBridge_Stuff(bridge_type, bridge_len)) {
- type2 = type;
- type = bridge_type;
- // We found two bridges, exit
- if (type2 != 0) break;
- }
- }
- // There is only one bridge that can be built
- if (type2 == 0 && type != 0) type2 = type;
-
- // Now, simply, build the bridge!
- if (_companies_ainew[c->index].tbt == AI_TRAIN) {
- return AI_DoCommand(tile_a, tile_b, type2 | RAILTYPE_RAIL << 8 | TRANSPORT_RAIL << 15, flag | DC_AUTO, CMD_BUILD_BRIDGE);
- } else {
- return AI_DoCommand(tile_a, tile_b, type2 | ROADTYPES_ROAD << 8 | TRANSPORT_ROAD << 15, flag | DC_AUTO, CMD_BUILD_BRIDGE);
- }
-}
-
-
-// Build the route part by part
-// Basicly what this function do, is build that amount of parts of the route
-// that go in the same direction. It sets 'part' to the last part of the route builded.
-// The return value is the cost for the builded parts
-//
-// Params:
-// PathFinderInfo : Pointer to the PathFinderInfo used for AiPathFinder
-// part : Which part we need to build
-//
-// TODO: skip already builded road-pieces (e.g.: cityroad)
-CommandCost AiNew_Build_RoutePart(Company *c, Ai_PathFinderInfo *PathFinderInfo, byte flag)
-{
- int part = PathFinderInfo->position;
- byte *route_extra = PathFinderInfo->route_extra;
- TileIndex *route = PathFinderInfo->route;
- int dir;
- int old_dir = -1;
- CommandCost cost;
- CommandCost res;
- // We need to calculate the direction with the parent of the parent.. so we skip
- // the first pieces and the last piece
- if (part < 1) part = 1;
- // When we are done, stop it
- if (part >= PathFinderInfo->route_length - 1) {
- PathFinderInfo->position = -2;
- return CommandCost();
- }
-
-
- if (PathFinderInfo->rail_or_road) {
- // Tunnel code
- if ((AI_PATHFINDER_FLAG_TUNNEL & route_extra[part]) != 0) {
- cost.AddCost(AI_DoCommand(route[part], 0, 0, flag, CMD_BUILD_TUNNEL));
- PathFinderInfo->position++;
- // TODO: problems!
- if (CmdFailed(cost)) {
- DEBUG(ai, 0, "[BuildPath] tunnel could not be built (0x%X)", route[part]);
- return CommandCost();
- }
- return cost;
- }
- // Bridge code
- if ((AI_PATHFINDER_FLAG_BRIDGE & route_extra[part]) != 0) {
- cost.AddCost(AiNew_Build_Bridge(c, route[part], route[part - 1], flag));
- PathFinderInfo->position++;
- // TODO: problems!
- if (CmdFailed(cost)) {
- DEBUG(ai, 0, "[BuildPath] bridge could not be built (0x%X, 0x%X)", route[part], route[part - 1]);
- return CommandCost();
- }
- return cost;
- }
-
- // Build normal rail
- // Keep it doing till we go an other way
- if (route_extra[part - 1] == 0 && route_extra[part] == 0) {
- while (route_extra[part] == 0) {
- // Get the current direction
- dir = AiNew_GetRailDirection(route[part - 1], route[part], route[part + 1]);
- // Is it the same as the last one?
- if (old_dir != -1 && old_dir != dir) break;
- old_dir = dir;
- // Build the tile
- res = AI_DoCommand(route[part], 0, dir, flag, CMD_BUILD_SINGLE_RAIL);
- if (CmdFailed(res)) {
- // Problem.. let's just abort it all!
- _companies_ainew[c->index].state = AI_STATE_NOTHING;
- return CommandCost();
- }
- cost.AddCost(res);
- // Go to the next tile
- part++;
- // Check if it is still in range..
- if (part >= PathFinderInfo->route_length - 1) break;
- }
- part--;
- }
- // We want to return the last position, so we go back one
- PathFinderInfo->position = part;
- } else {
- // Tunnel code
- if ((AI_PATHFINDER_FLAG_TUNNEL & route_extra[part]) != 0) {
- cost.AddCost(AI_DoCommand(route[part], 0x200 | ROADTYPES_ROAD, 0, flag, CMD_BUILD_TUNNEL));
- PathFinderInfo->position++;
- // TODO: problems!
- if (CmdFailed(cost)) {
- DEBUG(ai, 0, "[BuildPath] tunnel could not be built (0x%X)", route[part]);
- return CommandCost();
- }
- return cost;
- }
- // Bridge code
- if ((AI_PATHFINDER_FLAG_BRIDGE & route_extra[part]) != 0) {
- cost.AddCost(AiNew_Build_Bridge(c, route[part], route[part + 1], flag));
- PathFinderInfo->position++;
- // TODO: problems!
- if (CmdFailed(cost)) {
- DEBUG(ai, 0, "[BuildPath] bridge could not be built (0x%X, 0x%X)", route[part], route[part + 1]);
- return CommandCost();
- }
- return cost;
- }
-
- // Build normal road
- // Keep it doing till we go an other way
- // EnsureNoVehicleOnGround makes sure we don't build on a tile where a vehicle is. This way
- // it will wait till the vehicle is gone..
- if (route_extra[part - 1] == 0 && route_extra[part] == 0 && (flag != DC_EXEC || EnsureNoVehicleOnGround(route[part]))) {
- while (route_extra[part] == 0 && (flag != DC_EXEC || EnsureNoVehicleOnGround(route[part]))) {
- // Get the current direction
- dir = AiNew_GetRoadDirection(route[part - 1], route[part], route[part + 1]);
- // Is it the same as the last one?
- if (old_dir != -1 && old_dir != dir) break;
- old_dir = dir;
- // There is already some road, and it is a bridge.. don't build!!!
- if (!IsTileType(route[part], MP_TUNNELBRIDGE)) {
- // Build the tile
- res = AI_DoCommand(route[part], dir, 0, flag | DC_NO_WATER, CMD_BUILD_ROAD);
- // Currently, we ignore CMD_ERRORs!
- if (CmdFailed(res) && flag == DC_EXEC && !IsTileType(route[part], MP_ROAD) && !EnsureNoVehicleOnGround(route[part])) {
- // Problem.. let's just abort it all!
- DEBUG(ai, 0, "[BuidPath] route building failed at tile 0x%X, aborting", route[part]);
- _companies_ainew[c->index].state = AI_STATE_NOTHING;
- return CommandCost();
- }
-
- if (CmdSucceeded(res)) cost.AddCost(res);
- }
- // Go to the next tile
- part++;
- // Check if it is still in range..
- if (part >= PathFinderInfo->route_length - 1) break;
- }
- part--;
- // We want to return the last position, so we go back one
- }
- if (!EnsureNoVehicleOnGround(route[part]) && flag == DC_EXEC) part--;
- PathFinderInfo->position = part;
- }
-
- return cost;
-}
-
-
-// This functions tries to find the best vehicle for this type of cargo
-// It returns INVALID_ENGINE if not suitable engine is found
-EngineID AiNew_PickVehicle(Company *c)
-{
- if (_companies_ainew[c->index].tbt == AI_TRAIN) {
- // Not supported yet
- return INVALID_ENGINE;
- } else {
- EngineID best_veh_index = INVALID_ENGINE;
- int32 best_veh_rating = 0;
- const Engine *e;
-
- /* Loop through all road vehicles */
- FOR_ALL_ENGINES_OF_TYPE(e, VEH_ROAD) {
- EngineID i = e->index;
- const RoadVehicleInfo *rvi = &e->u.road;
-
- /* Skip vehicles which can't take our cargo type */
- if (rvi->cargo_type != _companies_ainew[c->index].cargo && !CanRefitTo(i, _companies_ainew[c->index].cargo)) continue;
-
- /* Skip trams */
- if (HasBit(EngInfo(i)->misc_flags, EF_ROAD_TRAM)) continue;
-
- // Is it availiable?
- // Also, check if the reliability of the vehicle is above the AI_VEHICLE_MIN_RELIABILTY
- if (!HasBit(e->company_avail, _current_company) || e->reliability * 100 < AI_VEHICLE_MIN_RELIABILTY << 16) continue;
-
- /* Rate and compare the engine by speed & capacity */
- int rating = rvi->max_speed * rvi->capacity;
- if (rating <= best_veh_rating) continue;
-
- // Can we build it?
- CommandCost ret = AI_DoCommand(0, i, 0, DC_QUERY_COST, CMD_BUILD_ROAD_VEH);
- if (CmdFailed(ret)) continue;
-
- best_veh_rating = rating;
- best_veh_index = i;
- }
-
- return best_veh_index;
- }
-}
-
-
-void CcAI(bool success, TileIndex tile, uint32 p1, uint32 p2)
-{
- Company *c = GetCompany(_current_company);
-
- if (success) {
- _companies_ainew[c->index].state = AI_STATE_GIVE_ORDERS;
- _companies_ainew[c->index].veh_id = _new_vehicle_id;
-
- if (GetVehicle(_companies_ainew[c->index].veh_id)->cargo_type != _companies_ainew[c->index].cargo) {
- /* Cargo type doesn't match, so refit it */
- if (CmdFailed(DoCommand(tile, _companies_ainew[c->index].veh_id, _companies_ainew[c->index].cargo, DC_EXEC, CMD_REFIT_ROAD_VEH))) {
- /* Refit failed, so sell the vehicle */
- DoCommand(tile, _companies_ainew[c->index].veh_id, 0, DC_EXEC, CMD_SELL_ROAD_VEH);
- _companies_ainew[c->index].state = AI_STATE_NOTHING;
- }
- }
- } else {
- /* XXX this should be handled more gracefully */
- _companies_ainew[c->index].state = AI_STATE_NOTHING;
- }
-}
-
-
-// Builds the best vehicle possible
-CommandCost AiNew_Build_Vehicle(Company *c, TileIndex tile, byte flag)
-{
- EngineID i = AiNew_PickVehicle(c);
-
- if (i == INVALID_ENGINE) return CMD_ERROR;
- if (_companies_ainew[c->index].tbt == AI_TRAIN) return CMD_ERROR;
-
- if (flag & DC_EXEC) {
- return AI_DoCommandCc(tile, i, 0, flag, CMD_BUILD_ROAD_VEH, CcAI);
- } else {
- return AI_DoCommand(tile, i, 0, flag, CMD_BUILD_ROAD_VEH);
- }
-}
-
-CommandCost AiNew_Build_Depot(Company *c, TileIndex tile, DiagDirection direction, byte flag)
-{
- CommandCost ret, ret2;
- if (_companies_ainew[c->index].tbt == AI_TRAIN) {
- return AI_DoCommand(tile, 0, direction, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_TRAIN_DEPOT);
- } else {
- ret = AI_DoCommand(tile, direction, 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD_DEPOT);
- if (CmdFailed(ret2)) return ret;
- // Try to build the road from the depot
- ret2 = AI_DoCommand(tile + TileOffsByDiagDir(direction), DiagDirToRoadBits(ReverseDiagDir(direction)), 0, flag | DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD);
- // If it fails, ignore it..
- if (CmdFailed(ret2)) return ret;
- ret.AddCost(ret2);
- return ret;
- }
-}
diff --git a/src/ai/trolly/pathfinder.cpp b/src/ai/trolly/pathfinder.cpp
deleted file mode 100644
index 0e08ddf77..000000000
--- a/src/ai/trolly/pathfinder.cpp
+++ /dev/null
@@ -1,482 +0,0 @@
-/* $Id$ */
-
-/** @file pathfinder.cpp Pathfinder support for the trolly AI. */
-
-#include "../../stdafx.h"
-#include "../../openttd.h"
-#include "../../bridge_map.h"
-#include "../../debug.h"
-#include "../../command_func.h"
-#include "trolly.h"
-#include "../../tunnel_map.h"
-#include "../../bridge.h"
-#include "../../tunnelbridge_map.h"
-#include "../ai.h"
-#include "../../variables.h"
-#include "../../company_base.h"
-#include "../../company_func.h"
-#include "../../tunnelbridge.h"
-
-
-#define TEST_STATION_NO_DIR 0xFF
-
-// Tests if a station can be build on the given spot
-// TODO: make it train compatible
-static bool TestCanBuildStationHere(TileIndex tile, byte dir)
-{
- Company *c = GetCompany(_current_company);
-
- if (dir == TEST_STATION_NO_DIR) {
- CommandCost ret;
- // TODO: currently we only allow spots that can be access from al 4 directions...
- // should be fixed!!!
- for (dir = 0; dir < 4; dir++) {
- ret = AiNew_Build_Station(c, _companies_ainew[c->index].tbt, tile, 1, 1, dir, DC_QUERY_COST);
- if (CmdSucceeded(ret)) return true;
- }
- return false;
- }
-
- // return true if command succeeded, so the inverse of CmdFailed()
- return CmdSucceeded(AiNew_Build_Station(c, _companies_ainew[c->index].tbt, tile, 1, 1, dir, DC_QUERY_COST));
-}
-
-
-static bool IsRoad(TileIndex tile)
-{
- return
- // MP_ROAD, but not a road depot?
- (IsTileType(tile, MP_ROAD) && !IsRoadDepot(tile)) ||
- (IsTileType(tile, MP_TUNNELBRIDGE) && GetTunnelBridgeTransportType(tile) == TRANSPORT_ROAD);
-}
-
-
-// Checks if a tile 'a' is between the tiles 'b' and 'c'
-#define TILES_BETWEEN(a, b, c) (TileX(a) >= TileX(b) && TileX(a) <= TileX(c) && TileY(a) >= TileY(b) && TileY(a) <= TileY(c))
-
-
-// Check if the current tile is in our end-area
-static int32 AyStar_AiPathFinder_EndNodeCheck(AyStar *aystar, OpenListNode *current)
-{
- const Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
-
- // It is not allowed to have a station on the end of a bridge or tunnel ;)
- if (current->path.node.user_data[0] != 0) return AYSTAR_DONE;
- if (TILES_BETWEEN(current->path.node.tile, PathFinderInfo->end_tile_tl, PathFinderInfo->end_tile_br))
- if (IsTileType(current->path.node.tile, MP_CLEAR) || IsTileType(current->path.node.tile, MP_TREES))
- if (current->path.parent == NULL || TestCanBuildStationHere(current->path.node.tile, AiNew_GetDirection(current->path.parent->node.tile, current->path.node.tile)))
- return AYSTAR_FOUND_END_NODE;
-
- return AYSTAR_DONE;
-}
-
-
-// Calculates the hash
-// Currently it is a 10 bit hash, so the hash array has a max depth of 6 bits (so 64)
-static uint AiPathFinder_Hash(uint key1, uint key2)
-{
- return (TileX(key1) & 0x1F) + ((TileY(key1) & 0x1F) << 5);
-}
-
-
-// Clear the memory of all the things
-static void AyStar_AiPathFinder_Free(AyStar *aystar)
-{
- AyStarMain_Free(aystar);
- delete aystar;
-}
-
-
-static int32 AyStar_AiPathFinder_CalculateG(AyStar *aystar, AyStarNode *current, OpenListNode *parent);
-static int32 AyStar_AiPathFinder_CalculateH(AyStar *aystar, AyStarNode *current, OpenListNode *parent);
-static void AyStar_AiPathFinder_FoundEndNode(AyStar *aystar, OpenListNode *current);
-static void AyStar_AiPathFinder_GetNeighbours(AyStar *aystar, OpenListNode *current);
-
-
-// This creates the AiPathFinder
-AyStar *new_AyStar_AiPathFinder(int max_tiles_around, Ai_PathFinderInfo *PathFinderInfo)
-{
- PathNode start_node;
- uint x;
- uint y;
- // Create AyStar
- AyStar *result = new AyStar();
- init_AyStar(result, AiPathFinder_Hash, 1 << 10);
- // Set the function pointers
- result->CalculateG = AyStar_AiPathFinder_CalculateG;
- result->CalculateH = AyStar_AiPathFinder_CalculateH;
- result->EndNodeCheck = AyStar_AiPathFinder_EndNodeCheck;
- result->FoundEndNode = AyStar_AiPathFinder_FoundEndNode;
- result->GetNeighbours = AyStar_AiPathFinder_GetNeighbours;
-
- result->free = AyStar_AiPathFinder_Free;
-
- // Set some information
- result->loops_per_tick = AI_PATHFINDER_LOOPS_PER_TICK;
- result->max_path_cost = 0;
- result->max_search_nodes = AI_PATHFINDER_MAX_SEARCH_NODES;
-
- // Set the user_data to the PathFinderInfo
- result->user_target = PathFinderInfo;
-
- // Set the start node
- start_node.parent = NULL;
- start_node.node.direction = INVALID_TRACKDIR;
- start_node.node.user_data[0] = 0;
-
- // Now we add all the starting tiles
- for (x = TileX(PathFinderInfo->start_tile_tl); x <= TileX(PathFinderInfo->start_tile_br); x++) {
- for (y = TileY(PathFinderInfo->start_tile_tl); y <= TileY(PathFinderInfo->start_tile_br); y++) {
- start_node.node.tile = TileXY(x, y);
- result->addstart(result, &start_node.node, 0);
- }
- }
-
- return result;
-}
-
-
-// To reuse AyStar we sometimes have to clean all the memory
-void clean_AyStar_AiPathFinder(AyStar *aystar, Ai_PathFinderInfo *PathFinderInfo)
-{
- PathNode start_node;
- uint x;
- uint y;
-
- aystar->clear(aystar);
-
- // Set the user_data to the PathFinderInfo
- aystar->user_target = PathFinderInfo;
-
- // Set the start node
- start_node.parent = NULL;
- start_node.node.direction = INVALID_TRACKDIR;
- start_node.node.user_data[0] = 0;
- start_node.node.tile = PathFinderInfo->start_tile_tl;
-
- // Now we add all the starting tiles
- for (x = TileX(PathFinderInfo->start_tile_tl); x <= TileX(PathFinderInfo->start_tile_br); x++) {
- for (y = TileY(PathFinderInfo->start_tile_tl); y <= TileY(PathFinderInfo->start_tile_br); y++) {
- TileIndex tile = TileXY(x, y);
-
- if (!IsTileType(tile, MP_CLEAR) && !IsTileType(tile, MP_TREES)) continue;
- if (!TestCanBuildStationHere(tile, TEST_STATION_NO_DIR)) continue;
- start_node.node.tile = tile;
- aystar->addstart(aystar, &start_node.node, 0);
- }
- }
-}
-
-
-// The h-value, simple calculation
-static int32 AyStar_AiPathFinder_CalculateH(AyStar *aystar, AyStarNode *current, OpenListNode *parent)
-{
- const Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
- int r, r2;
-
- if (PathFinderInfo->end_direction != AI_PATHFINDER_NO_DIRECTION) {
- // The station is pointing to a direction, add a tile towards that direction, so the H-value is more accurate
- r = DistanceManhattan(current->tile, PathFinderInfo->end_tile_tl + TileOffsByDiagDir(PathFinderInfo->end_direction));
- r2 = DistanceManhattan(current->tile, PathFinderInfo->end_tile_br + TileOffsByDiagDir(PathFinderInfo->end_direction));
- } else {
- // No direction, so just get the fastest route to the station
- r = DistanceManhattan(current->tile, PathFinderInfo->end_tile_tl);
- r2 = DistanceManhattan(current->tile, PathFinderInfo->end_tile_br);
- }
- // See if the bottomright is faster than the topleft..
- if (r2 < r) r = r2;
- return r * AI_PATHFINDER_H_MULTIPLER;
-}
-
-
-// We found the end.. let's get the route back and put it in an array
-static void AyStar_AiPathFinder_FoundEndNode(AyStar *aystar, OpenListNode *current)
-{
- Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
- uint i = 0;
- PathNode *parent = &current->path;
-
- do {
- PathFinderInfo->route_extra[i] = parent->node.user_data[0];
- PathFinderInfo->route[i++] = parent->node.tile;
- if (i > lengthof(PathFinderInfo->route)) {
- // We ran out of space for the PathFinder
- DEBUG(ai, 0, "No more space in pathfinder route[] array");
- PathFinderInfo->route_length = -1; // -1 indicates out of space
- return;
- }
- parent = parent->parent;
- } while (parent != NULL);
- PathFinderInfo->route_length = i;
- DEBUG(ai, 1, "Found route of %d nodes long in %d nodes of searching", i, Hash_Size(&aystar->ClosedListHash));
-}
-
-
-// What tiles are around us.
-static void AyStar_AiPathFinder_GetNeighbours(AyStar *aystar, OpenListNode *current)
-{
- CommandCost ret;
- int dir;
-
- Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
-
- aystar->num_neighbours = 0;
-
- // Go through all surrounding tiles and check if they are within the limits
- for (DiagDirection i = DIAGDIR_BEGIN; i < DIAGDIR_END; i++) {
- TileIndex ctile = current->path.node.tile; // Current tile
- TileIndex atile = ctile + TileOffsByDiagDir(i); // Adjacent tile
-
- if (TileX(atile) > 1 && TileX(atile) < MapMaxX() - 1 &&
- TileY(atile) > 1 && TileY(atile) < MapMaxY() - 1) {
- // We also directly test if the current tile can connect to this tile..
- // We do this simply by just building the tile!
-
- // If the next step is a bridge, we have to enter it the right way
- if (!PathFinderInfo->rail_or_road && IsRoad(atile)) {
- if (IsTileType(atile, MP_TUNNELBRIDGE)) {
- if (GetTunnelBridgeDirection(atile) != i) continue;
- }
- }
-
- if ((AI_PATHFINDER_FLAG_BRIDGE & current->path.node.user_data[0]) != 0 ||
- (AI_PATHFINDER_FLAG_TUNNEL & current->path.node.user_data[0]) != 0) {
- // We are a bridge/tunnel, how cool!!
- // This means we can only point forward.. get the direction from the user_data
- if ((uint)i != (current->path.node.user_data[0] >> 8)) continue;
- }
- dir = 0;
-
- // First, check if we have a parent
- if (current->path.parent == NULL && current->path.node.user_data[0] == 0) {
- // If not, this means we are at the starting station
- if (PathFinderInfo->start_direction != AI_PATHFINDER_NO_DIRECTION) {
- // We do need a direction?
- if (AiNew_GetDirection(ctile, atile) != PathFinderInfo->start_direction) {
- // We are not pointing the right way, invalid tile
- continue;
- }
- }
- } else if (current->path.node.user_data[0] == 0) {
- if (PathFinderInfo->rail_or_road) {
- // Rail check
- dir = AiNew_GetRailDirection(current->path.parent->node.tile, ctile, atile);
- ret = AI_DoCommand(ctile, 0, dir, DC_AUTO | DC_NO_WATER, CMD_BUILD_SINGLE_RAIL);
- if (CmdFailed(ret)) continue;
-#ifdef AI_PATHFINDER_NO_90DEGREES_TURN
- if (current->path.parent->parent != NULL) {
- // Check if we don't make a 90degree curve
- int dir1 = AiNew_GetRailDirection(current->path.parent->parent->node.tile, current->path.parent->node.tile, ctile);
- if (_illegal_curves[dir1] == dir || _illegal_curves[dir] == dir1) {
- continue;
- }
- }
-#endif
- } else {
- // Road check
- dir = AiNew_GetRoadDirection(current->path.parent->node.tile, ctile, atile);
- if (IsRoad(ctile)) {
- if (IsTileType(ctile, MP_TUNNELBRIDGE)) {
- // We have a bridge, how nicely! We should mark it...
- dir = 0;
- } else {
- // It already has road.. check if we miss any bits!
- if ((GetAnyRoadBits(ctile, ROADTYPE_ROAD) & dir) != dir) {
- // We do miss some pieces :(
- dir &= ~GetAnyRoadBits(ctile, ROADTYPE_ROAD);
- } else {
- dir = 0;
- }
- }
- }
- // Only destruct things if it is MP_CLEAR of MP_TREES
- if (dir != 0) {
- ret = AI_DoCommand(ctile, dir, 0, DC_AUTO | DC_NO_WATER, CMD_BUILD_ROAD);
- if (CmdFailed(ret)) continue;
- }
- }
- }
-
- // The tile can be connected
- aystar->neighbours[aystar->num_neighbours].tile = atile;
- aystar->neighbours[aystar->num_neighbours].user_data[0] = 0;
- aystar->neighbours[aystar->num_neighbours++].direction = INVALID_TRACKDIR;
- }
- }
-
- // Next step, check for bridges and tunnels
- if (current->path.parent != NULL && current->path.node.user_data[0] == 0) {
- // First we get the dir from this tile and his parent
- DiagDirection dir = AiNew_GetDirection(current->path.parent->node.tile, current->path.node.tile);
- // It means we can only walk with the track, so the bridge has to be in the same direction
- TileIndex tile = current->path.node.tile;
- TileIndex new_tile = tile;
- Slope tileh = GetTileSlope(tile, NULL);
-
- // Bridges can only be build on land that is not flat
- // And if there is a road or rail blocking
- if (tileh != SLOPE_FLAT ||
- (PathFinderInfo->rail_or_road && IsTileType(tile + TileOffsByDiagDir(dir), MP_ROAD)) ||
- (!PathFinderInfo->rail_or_road && IsTileType(tile + TileOffsByDiagDir(dir), MP_RAILWAY))) {
- for (;;) {
- new_tile += TileOffsByDiagDir(dir);
-
- // Precheck, is the length allowed?
- if (!CheckBridge_Stuff(0, GetTunnelBridgeLength(tile, new_tile))) break;
-
- // Check if we hit the station-tile.. we don't like that!
- if (TILES_BETWEEN(new_tile, PathFinderInfo->end_tile_tl, PathFinderInfo->end_tile_br)) break;
-
- // Try building the bridge..
- ret = AI_DoCommand(tile, new_tile, (0 << 8) + (MAX_BRIDGES / 2), DC_AUTO, CMD_BUILD_BRIDGE);
- if (CmdFailed(ret)) continue;
- // We can build a bridge here.. add him to the neighbours
- aystar->neighbours[aystar->num_neighbours].tile = new_tile;
- aystar->neighbours[aystar->num_neighbours].user_data[0] = AI_PATHFINDER_FLAG_BRIDGE + (dir << 8);
- aystar->neighbours[aystar->num_neighbours++].direction = INVALID_TRACKDIR;
- // We can only have 12 neighbours, and we need 1 left for tunnels
- if (aystar->num_neighbours == 11) break;
- }
- }
-
- // Next, check for tunnels!
- // Tunnels can only be built on slopes corresponding to the direction
- // For now, we check both sides for this tile.. terraforming gives fuzzy result
- if (tileh == InclinedSlope(dir)) {
- // Now simply check if a tunnel can be build
- ret = AI_DoCommand(tile, (PathFinderInfo->rail_or_road ? 0 : 0x200), 0, DC_AUTO, CMD_BUILD_TUNNEL);
- tileh = GetTileSlope(_build_tunnel_endtile, NULL);
- if (CmdSucceeded(ret) && IsInclinedSlope(tileh)) {
- aystar->neighbours[aystar->num_neighbours].tile = _build_tunnel_endtile;
- aystar->neighbours[aystar->num_neighbours].user_data[0] = AI_PATHFINDER_FLAG_TUNNEL + (dir << 8);
- aystar->neighbours[aystar->num_neighbours++].direction = INVALID_TRACKDIR;
- }
- }
- }
-}
-
-
-extern Foundation GetRailFoundation(Slope tileh, TrackBits bits); // XXX function declaration in .c
-extern Foundation GetRoadFoundation(Slope tileh, RoadBits bits); // XXX function declaration in .c
-
-// The most important function: it calculates the g-value
-static int32 AyStar_AiPathFinder_CalculateG(AyStar *aystar, AyStarNode *current, OpenListNode *parent)
-{
- Ai_PathFinderInfo *PathFinderInfo = (Ai_PathFinderInfo*)aystar->user_target;
- int res = 0;
- Slope tileh = GetTileSlope(current->tile, NULL);
- Slope parent_tileh = GetTileSlope(parent->path.node.tile, NULL);
-
- // Check if we hit the end-tile
- if (TILES_BETWEEN(current->tile, PathFinderInfo->end_tile_tl, PathFinderInfo->end_tile_br)) {
- // We are at the end-tile, check if we had a direction or something...
- if (PathFinderInfo->end_direction != AI_PATHFINDER_NO_DIRECTION && AiNew_GetDirection(current->tile, parent->path.node.tile) != PathFinderInfo->end_direction) {
- // We are not pointing the right way, invalid tile
- return AYSTAR_INVALID_NODE;
- }
- // If it was valid, drop out.. we don't build on the endtile
- return 0;
- }
-
- // Give everything a small penalty
- res += AI_PATHFINDER_PENALTY;
-
- if (!PathFinderInfo->rail_or_road) {
- // Road has the lovely advantage it can use other road... check if
- // the current tile is road, and if so, give a good bonus
- if (IsRoad(current->tile)) {
- res -= AI_PATHFINDER_ROAD_ALREADY_EXISTS_BONUS;
- }
- }
-
- // We should give a penalty when the tile is going up or down.. this is one way to do so!
- // Too bad we have to count it from the parent.. but that is not so bad.
- // We also dislike long routes on slopes, since they do not look too realistic
- // when there is a flat land all around, they are more expensive to build, and
- // especially they essentially block the ability to connect or cross the road
- // from one side.
- if (parent_tileh != SLOPE_FLAT && parent->path.parent != NULL) {
- // Skip if the tile was from a bridge or tunnel
- if (parent->path.node.user_data[0] == 0 && current->user_data[0] == 0) {
- if (PathFinderInfo->rail_or_road) {
- Foundation f = GetRailFoundation(parent_tileh, (TrackBits)(1 << AiNew_GetRailDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile)));
- if (IsInclinedFoundation(f) || (!IsFoundation(f) && IsInclinedSlope(parent_tileh))) {
- res += AI_PATHFINDER_TILE_GOES_UP_PENALTY;
- } else {
- res += AI_PATHFINDER_FOUNDATION_PENALTY;
- }
- } else {
- if (!IsRoad(parent->path.node.tile) || !IsTileType(parent->path.node.tile, MP_TUNNELBRIDGE)) {
- Foundation f = GetRoadFoundation(parent_tileh, (RoadBits)AiNew_GetRoadDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile));
- if (IsInclinedFoundation(f) || (!IsFoundation(f) && IsInclinedSlope(parent_tileh))) {
- res += AI_PATHFINDER_TILE_GOES_UP_PENALTY;
- } else {
- res += AI_PATHFINDER_FOUNDATION_PENALTY;
- }
- }
- }
- }
- }
-
- // Are we part of a tunnel?
- if ((AI_PATHFINDER_FLAG_TUNNEL & current->user_data[0]) != 0) {
- int r;
- // Tunnels are very expensive when build on long routes..
- // Ironicly, we are using BridgeCode here ;)
- r = AI_PATHFINDER_TUNNEL_PENALTY * GetTunnelBridgeLength(current->tile, parent->path.node.tile);
- res += r + (r >> 8);
- }
-
- // Are we part of a bridge?
- if ((AI_PATHFINDER_FLAG_BRIDGE & current->user_data[0]) != 0) {
- // That means for every length a penalty
- res += AI_PATHFINDER_BRIDGE_PENALTY * GetTunnelBridgeLength(current->tile, parent->path.node.tile);
- // Check if we are going up or down, first for the starting point
- // In user_data[0] is at the 8th bit the direction
- if (!HasBridgeFlatRamp(parent_tileh, (Axis)((current->user_data[0] >> 8) & 1))) res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
- // Second for the end point
- if (!HasBridgeFlatRamp(tileh, (Axis)((current->user_data[0] >> 8) & 1))) res += AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY;
- }
-
- // To prevent the AI from taking the fastest way in tiles, but not the fastest way
- // in speed, we have to give a good penalty to direction changing
- // This way, we get almost the fastest way in tiles, and a very good speed on the track
- if (!PathFinderInfo->rail_or_road) {
- if (parent->path.parent != NULL &&
- AiNew_GetDirection(current->tile, parent->path.node.tile) != AiNew_GetDirection(parent->path.node.tile, parent->path.parent->node.tile)) {
- // When road exists, we don't like turning, but its free, so don't be to piggy about it
- if (IsRoad(parent->path.node.tile)) {
- res += AI_PATHFINDER_DIRECTION_CHANGE_ON_EXISTING_ROAD_PENALTY;
- } else {
- res += AI_PATHFINDER_DIRECTION_CHANGE_PENALTY;
- }
- }
- } else {
- // For rail we have 1 exeption: diagonal rail..
- // So we fetch 2 raildirection. That of the current one, and of the one before that
- if (parent->path.parent != NULL && parent->path.parent->parent != NULL) {
- int dir1 = AiNew_GetRailDirection(parent->path.parent->node.tile, parent->path.node.tile, current->tile);
- int dir2 = AiNew_GetRailDirection(parent->path.parent->parent->node.tile, parent->path.parent->node.tile, parent->path.node.tile);
- // First, see if we are on diagonal path, that is better than straight path
- if (dir1 > 1) res -= AI_PATHFINDER_DIAGONAL_BONUS;
-
- // First see if they are different
- if (dir1 != dir2) {
- // dir 2 and 3 are 1 diagonal track, and 4 and 5.
- if (!(((dir1 == 2 || dir1 == 3) && (dir2 == 2 || dir2 == 3)) || ((dir1 == 4 || dir1 == 5) && (dir2 == 4 || dir2 == 5)))) {
- // It is not, so we changed of direction
- res += AI_PATHFINDER_DIRECTION_CHANGE_PENALTY;
- }
- if (parent->path.parent->parent->parent != NULL) {
- int dir3 = AiNew_GetRailDirection(parent->path.parent->parent->parent->node.tile, parent->path.parent->parent->node.tile, parent->path.parent->node.tile);
- // Check if we changed 3 tiles of direction in 3 tiles.. bad!!!
- if ((dir1 == 0 || dir1 == 1) && dir2 > 1 && (dir3 == 0 || dir3 == 1)) {
- res += AI_PATHFINDER_CURVE_PENALTY;
- }
- }
- }
- }
- }
-
- return (res < 0) ? 0 : res;
-}
diff --git a/src/ai/trolly/shared.cpp b/src/ai/trolly/shared.cpp
deleted file mode 100644
index 1183f0371..000000000
--- a/src/ai/trolly/shared.cpp
+++ /dev/null
@@ -1,121 +0,0 @@
-/* $Id$ */
-
-/** @file shared.cpp Shared functions for the trolly AI. */
-
-#include "../../stdafx.h"
-#include "../../openttd.h"
-#include "../../debug.h"
-#include "../../map_func.h"
-#include "../../vehicle_base.h"
-#include "../../company_base.h"
-#include "trolly.h"
-
-int AiNew_GetRailDirection(TileIndex tile_a, TileIndex tile_b, TileIndex tile_c)
-{
- // 0 = vert
- // 1 = horz
- // 2 = dig up-left
- // 3 = dig down-right
- // 4 = dig down-left
- // 5 = dig up-right
-
- uint x1 = TileX(tile_a);
- uint x2 = TileX(tile_b);
- uint x3 = TileX(tile_c);
-
- uint y1 = TileY(tile_a);
- uint y2 = TileY(tile_b);
- uint y3 = TileY(tile_c);
-
- if (y1 == y2 && y2 == y3) return 0;
- if (x1 == x2 && x2 == x3) return 1;
- if (y2 > y1) return x2 > x3 ? 2 : 4;
- if (x2 > x1) return y2 > y3 ? 2 : 5;
- if (y1 > y2) return x2 > x3 ? 5 : 3;
- if (x1 > x2) return y2 > y3 ? 4 : 3;
-
- return 0;
-}
-
-int AiNew_GetRoadDirection(TileIndex tile_a, TileIndex tile_b, TileIndex tile_c)
-{
- int x1, x2, x3;
- int y1, y2, y3;
- int r;
-
- x1 = TileX(tile_a);
- x2 = TileX(tile_b);
- x3 = TileX(tile_c);
-
- y1 = TileY(tile_a);
- y2 = TileY(tile_b);
- y3 = TileY(tile_c);
-
- r = 0;
-
- if (x1 < x2) r += 8;
- if (y1 < y2) r += 1;
- if (x1 > x2) r += 2;
- if (y1 > y2) r += 4;
-
- if (x2 < x3) r += 2;
- if (y2 < y3) r += 4;
- if (x2 > x3) r += 8;
- if (y2 > y3) r += 1;
-
- return r;
-}
-
-// Get's the direction between 2 tiles seen from tile_a
-DiagDirection AiNew_GetDirection(TileIndex tile_a, TileIndex tile_b)
-{
- if (TileY(tile_a) < TileY(tile_b)) return DIAGDIR_SE;
- if (TileY(tile_a) > TileY(tile_b)) return DIAGDIR_NW;
- if (TileX(tile_a) < TileX(tile_b)) return DIAGDIR_SW;
- return DIAGDIR_NE;
-}
-
-
-// This functions looks up if this vehicle is special for this AI
-// and returns his flag
-uint AiNew_GetSpecialVehicleFlag(Company *c, Vehicle *v)
-{
- uint i;
-
- for (i = 0; i < AI_MAX_SPECIAL_VEHICLES; i++) {
- if (_companies_ainew[c->index].special_vehicles[i].veh_id == v->index) {
- return _companies_ainew[c->index].special_vehicles[i].flag;
- }
- }
-
- // Not found :(
- return 0;
-}
-
-
-bool AiNew_SetSpecialVehicleFlag(Company *c, Vehicle *v, uint flag)
-{
- int new_id = -1;
- uint i;
-
- for (i = 0; i < AI_MAX_SPECIAL_VEHICLES; i++) {
- if (_companies_ainew[c->index].special_vehicles[i].veh_id == v->index) {
- _companies_ainew[c->index].special_vehicles[i].flag |= flag;
- return true;
- }
- if (new_id == -1 &&
- _companies_ainew[c->index].special_vehicles[i].veh_id == 0 &&
- _companies_ainew[c->index].special_vehicles[i].flag == 0) {
- new_id = i;
- }
- }
-
- // Out of special_vehicle spots :s
- if (new_id == -1) {
- DEBUG(ai, 1, "special_vehicles list is too small");
- return false;
- }
- _companies_ainew[c->index].special_vehicles[new_id].veh_id = v->index;
- _companies_ainew[c->index].special_vehicles[new_id].flag = flag;
- return true;
-}
diff --git a/src/ai/trolly/trolly.cpp b/src/ai/trolly/trolly.cpp
deleted file mode 100644
index 6b874ec0d..000000000
--- a/src/ai/trolly/trolly.cpp
+++ /dev/null
@@ -1,1348 +0,0 @@
-/* $Id$ */
-
-/**
- * @file trolly.cpp Implementation of the trolly AI.
- *
- * This AI was created as a direct reaction to the big demand for some good AIs
- * in OTTD. Too bad it never left alpha-stage, and it is considered dead in its
- * current form.
- * By the time of writing this, we, the creator of this AI and a good friend of
- * mine, are designing a whole new AI-system that allows us to create AIs
- * easier and without all the fuzz we encountered while I was working on this
- * AI. By the time that system is finished, you can expect that this AI will
- * dissapear, because it is pretty obselete and bad programmed.
- *
- * Meanwhile I wish you all much fun with this AI; if you are interested as
- * AI-developer in this AI, I advise you not stare too long to some code, some
- * things in here really are... strange ;) But in either way: enjoy :)
- *
- * -- TrueLight :: 2005-09-01
- */
-
-#include "../../stdafx.h"
-#include "../../openttd.h"
-#include "../../debug.h"
-#include "../../road_map.h"
-#include "../../station_map.h"
-#include "../../command_func.h"
-#include "trolly.h"
-#include "../../depot_base.h"
-#include "../../town.h"
-#include "../../industry.h"
-#include "../../station_base.h"
-#include "../../engine_func.h"
-#include "../../engine_base.h"
-#include "../../gui.h"
-#include "../../vehicle_base.h"
-#include "../../vehicle_func.h"
-#include "../../date_func.h"
-#include "../ai.h"
-#include "../../company_base.h"
-#include "../../company_func.h"
-
-#include "table/strings.h"
-
-CompanyAiNew _companies_ainew[MAX_COMPANIES];
-
-// This function is called after StartUp. It is the init of an AI
-static void AiNew_State_FirstTime(Company *c)
-{
- // This assert is used to protect those function from misuse
- // You have quickly a small mistake in the state-array
- // With that, everything would go wrong. Finding that, is almost impossible
- // With this assert, that problem can never happen.
- assert(_companies_ainew[c->index].state == AI_STATE_FIRST_TIME);
- // We first have to init some things
-
- if (_current_company == 1) ShowErrorMessage(INVALID_STRING_ID, TEMP_AI_IN_PROGRESS, 0, 0);
-
- // The PathFinder (AyStar)
- // TODO: Maybe when an AI goes bankrupt, this is de-init
- // or when coming from a savegame.. should be checked out!
- _companies_ainew[c->index].path_info.start_tile_tl = 0;
- _companies_ainew[c->index].path_info.start_tile_br = 0;
- _companies_ainew[c->index].path_info.end_tile_tl = 0;
- _companies_ainew[c->index].path_info.end_tile_br = 0;
- _companies_ainew[c->index].pathfinder = new_AyStar_AiPathFinder(12, &_companies_ainew[c->index].path_info);
-
- _companies_ainew[c->index].idle = 0;
- _companies_ainew[c->index].last_vehiclecheck_date = _date;
-
- // We ALWAYS start with a bus route.. just some basic money ;)
- _companies_ainew[c->index].action = AI_ACTION_BUS_ROUTE;
-
- // Let's popup the news, and after that, start building..
- _companies_ainew[c->index].state = AI_STATE_WAKE_UP;
-}
-
-
-// This function just waste some time
-// It keeps it more real. The AI can build on such tempo no normal user
-// can ever keep up with that. The competitor_speed already delays a bit
-// but after the AI finished a track it really needs to go to sleep.
-//
-// Let's say, we sleep between one and three days if the AI is put on Very Fast.
-// This means that on Very Slow it will be between 16 and 48 days.. slow enough?
-static void AiNew_State_Nothing(Company *c)
-{
- assert(_companies_ainew[c->index].state == AI_STATE_NOTHING);
- // If we are done idling, start over again
- if (_companies_ainew[c->index].idle == 0) _companies_ainew[c->index].idle = AI_RandomRange(DAY_TICKS * 2) + DAY_TICKS;
- if (--_companies_ainew[c->index].idle == 0) {
- // We are done idling.. what you say? Let's do something!
- // I mean.. the next tick ;)
- _companies_ainew[c->index].state = AI_STATE_WAKE_UP;
- }
-}
-
-
-// This function picks out a task we are going to do.
-// Currently supported:
-// - Make new route
-// - Check route
-// - Build HQ
-static void AiNew_State_WakeUp(Company *c)
-{
- assert(_companies_ainew[c->index].state == AI_STATE_WAKE_UP);
- // First, check if we have a HQ
- if (c->location_of_HQ == INVALID_TILE) {
- // We have no HQ yet, build one on a random place
- // Random till we found a place for it!
- // TODO: this should not be on a random place..
- AiNew_Build_CompanyHQ(c, AI_Random() % MapSize());
- // Enough for now, but we want to come back here the next time
- // so we do not change any status
- return;
- }
-
- Money money = c->money - AI_MINIMUM_MONEY;
-
- // Let's pick an action!
- if (_companies_ainew[c->index].action == AI_ACTION_NONE) {
- int r = AI_Random() & 0xFF;
- if (c->current_loan > 0 &&
- c->old_economy[1].income > AI_MINIMUM_INCOME_FOR_LOAN &&
- r < 10) {
- _companies_ainew[c->index].action = AI_ACTION_REPAY_LOAN;
- } else if (_companies_ainew[c->index].last_vehiclecheck_date + AI_DAYS_BETWEEN_VEHICLE_CHECKS < _date) {
- // Check all vehicles once in a while
- _companies_ainew[c->index].action = AI_ACTION_CHECK_ALL_VEHICLES;
- _companies_ainew[c->index].last_vehiclecheck_date = _date;
- } else if (r < 100 && !_settings_game.ai.ai_disable_veh_roadveh) {
- // Do we have any spots for road-vehicles left open?
- if (GetFreeUnitNumber(VEH_ROAD) <= _settings_game.vehicle.max_roadveh) {
- if (r < 85) {
- _companies_ainew[c->index].action = AI_ACTION_TRUCK_ROUTE;
- } else {
- _companies_ainew[c->index].action = AI_ACTION_BUS_ROUTE;
- }
- }
-#if 0
- } else if (r < 200 && !_settings_game.ai.ai_disable_veh_train) {
- if (GetFreeUnitNumber(VEH_TRAIN) <= _settings_game.vehicle.max_trains) {
- _companies_ainew[c->index].action = AI_ACTION_TRAIN_ROUTE;
- }
-#endif
- }
-
- _companies_ainew[c->index].counter = 0;
- }
-
- if (_companies_ainew[c->index].counter++ > AI_MAX_TRIES_FOR_SAME_ROUTE) {
- _companies_ainew[c->index].action = AI_ACTION_NONE;
- return;
- }
-
- if (_settings_game.ai.ai_disable_veh_roadveh && (
- _companies_ainew[c->index].action == AI_ACTION_BUS_ROUTE ||
- _companies_ainew[c->index].action == AI_ACTION_TRUCK_ROUTE
- )) {
- _companies_ainew[c->index].action = AI_ACTION_NONE;
- return;
- }
-
- if (_companies_ainew[c->index].action == AI_ACTION_REPAY_LOAN &&
- money > AI_MINIMUM_LOAN_REPAY_MONEY) {
- // We start repaying some money..
- _companies_ainew[c->index].state = AI_STATE_REPAY_MONEY;
- return;
- }
-
- if (_companies_ainew[c->index].action == AI_ACTION_CHECK_ALL_VEHICLES) {
- _companies_ainew[c->index].state = AI_STATE_CHECK_ALL_VEHICLES;
- return;
- }
-
- // It is useless to start finding a route if we don't have enough money
- // to build the route anyway..
- if (_companies_ainew[c->index].action == AI_ACTION_BUS_ROUTE &&
- money > AI_MINIMUM_BUS_ROUTE_MONEY) {
- if (GetFreeUnitNumber(VEH_ROAD) > _settings_game.vehicle.max_roadveh) {
- _companies_ainew[c->index].action = AI_ACTION_NONE;
- return;
- }
- _companies_ainew[c->index].cargo = AI_NEED_CARGO;
- _companies_ainew[c->index].state = AI_STATE_LOCATE_ROUTE;
- _companies_ainew[c->index].tbt = AI_BUS; // Bus-route
- return;
- }
- if (_companies_ainew[c->index].action == AI_ACTION_TRUCK_ROUTE &&
- money > AI_MINIMUM_TRUCK_ROUTE_MONEY) {
- if (GetFreeUnitNumber(VEH_ROAD) > _settings_game.vehicle.max_roadveh) {
- _companies_ainew[c->index].action = AI_ACTION_NONE;
- return;
- }
- _companies_ainew[c->index].cargo = AI_NEED_CARGO;
- _companies_ainew[c->index].last_id = 0;
- _companies_ainew[c->index].state = AI_STATE_LOCATE_ROUTE;
- _companies_ainew[c->index].tbt = AI_TRUCK;
- return;
- }
-
- _companies_ainew[c->index].state = AI_STATE_NOTHING;
-}
-
-
-static void AiNew_State_ActionDone(Company *c)
-{
- _companies_ainew[c->index].action = AI_ACTION_NONE;
- _companies_ainew[c->index].state = AI_STATE_NOTHING;
-}
-
-
-// Check if a city or industry is good enough to start a route there
-static bool AiNew_Check_City_or_Industry(Company *c, int ic, byte type)
-{
- if (type == AI_CITY) {
- const Town *t = GetTown(ic);
- const Station *st;
- uint count = 0;
- int j = 0;
-
- // We don't like roadconstructions, don't even true such a city
- if (t->road_build_months != 0) return false;
-
- // Check if the rating in a city is high enough
- // If not, take a chance if we want to continue
- if (t->ratings[_current_company] < 0 && AI_CHANCE16(1, 4)) return false;
-
- if (t->max_pass - t->act_pass < AI_CHECKCITY_NEEDED_CARGO && !AI_CHANCE16(1, AI_CHECKCITY_CITY_CHANCE)) return false;
-
- // Check if we have build a station in this town the last 6 months
- // else we don't do it. This is done, because stat updates can be slow
- // and sometimes it takes up to 4 months before the stats are corectly.
- // This way we don't get 12 busstations in one city of 100 population ;)
- FOR_ALL_STATIONS(st) {
- // Do we own it?
- if (st->owner == _current_company) {
- // Are we talking busses?
- if (_companies_ainew[c->index].tbt == AI_BUS && (FACIL_BUS_STOP & st->facilities) != FACIL_BUS_STOP) continue;
- // Is it the same city as we are in now?
- if (st->town != t) continue;
- // When was this station build?
- if (_date - st->build_date < AI_CHECKCITY_DATE_BETWEEN) return false;
- // Cound the amount of stations in this city that we own
- count++;
- } else {
- // We do not own it, request some info about the station
- // we want to know if this station gets the same good. If so,
- // we want to know its rating. If it is too high, we are not going
- // to build there
- if (!st->goods[CT_PASSENGERS].last_speed) continue;
- // Is it around our city
- if (DistanceManhattan(st->xy, t->xy) > 10) continue;
- // It does take this cargo.. what is his rating?
- if (st->goods[CT_PASSENGERS].rating < AI_CHECKCITY_CARGO_RATING) continue;
- j++;
- // When this is the first station, we build a second with no problem ;)
- if (j == 1) continue;
- // The rating is high.. second station...
- // a little chance that we still continue
- // But if there are 3 stations of this size, we never go on...
- if (j == 2 && AI_CHANCE16(1, AI_CHECKCITY_CARGO_RATING_CHANCE)) continue;
- // We don't like this station :(
- return false;
- }
- }
-
- // We are about to add one...
- count++;
- // Check if we the city can provide enough cargo for this amount of stations..
- if (count * AI_CHECKCITY_CARGO_PER_STATION > t->max_pass) return false;
-
- // All check are okay, so we can build here!
- return true;
- }
- if (type == AI_INDUSTRY) {
- const Industry *i = GetIndustry(ic);
- const Station *st;
- int count = 0;
- int j = 0;
-
- if (i->town != NULL && i->town->ratings[_current_company] < 0 && AI_CHANCE16(1, 4)) return false;
-
- // No limits on delevering stations!
- // Or for industry that does not give anything yet
- if (i->produced_cargo[0] == CT_INVALID || i->last_month_production[0] == 0) return true;
-
- if (i->last_month_production[0] - i->last_month_transported[0] < AI_CHECKCITY_NEEDED_CARGO) return false;
-
- // Check if we have build a station in this town the last 6 months
- // else we don't do it. This is done, because stat updates can be slow
- // and sometimes it takes up to 4 months before the stats are corectly.
- FOR_ALL_STATIONS(st) {
- // Do we own it?
- if (st->owner == _current_company) {
- // Are we talking trucks?
- if (_companies_ainew[c->index].tbt == AI_TRUCK && (FACIL_TRUCK_STOP & st->facilities) != FACIL_TRUCK_STOP) continue;
- // Is it the same city as we are in now?
- if (st->town != i->town) continue;
- // When was this station build?
- if (_date - st->build_date < AI_CHECKCITY_DATE_BETWEEN) return false;
- // Cound the amount of stations in this city that we own
- count++;
- } else {
- // We do not own it, request some info about the station
- // we want to know if this station gets the same good. If so,
- // we want to know its rating. If it is too high, we are not going
- // to build there
- if (i->produced_cargo[0] == CT_INVALID) continue;
- // It does not take this cargo
- if (!st->goods[i->produced_cargo[0]].last_speed) continue;
- // Is it around our industry
- if (DistanceManhattan(st->xy, i->xy) > 5) continue;
- // It does take this cargo.. what is his rating?
- if (st->goods[i->produced_cargo[0]].rating < AI_CHECKCITY_CARGO_RATING) continue;
- j++;
- // The rating is high.. a little chance that we still continue
- // But if there are 2 stations of this size, we never go on...
- if (j == 1 && AI_CHANCE16(1, AI_CHECKCITY_CARGO_RATING_CHANCE)) continue;
- // We don't like this station :(
- return false;
- }
- }
-
- // We are about to add one...
- count++;
- // Check if we the city can provide enough cargo for this amount of stations..
- if (count * AI_CHECKCITY_CARGO_PER_STATION > i->last_month_production[0]) return false;
-
- // All check are okay, so we can build here!
- return true;
- }
-
- return true;
-}
-
-
-// This functions tries to locate a good route
-static void AiNew_State_LocateRoute(Company *c)
-{
- assert(_companies_ainew[c->index].state == AI_STATE_LOCATE_ROUTE);
- // For now, we only support PASSENGERS, CITY and BUSSES
-
- // We don't have a route yet
- if (_companies_ainew[c->index].cargo == AI_NEED_CARGO) {
- _companies_ainew[c->index].new_cost = 0; // No cost yet
- _companies_ainew[c->index].temp = -1;
- // Reset the counter
- _companies_ainew[c->index].counter = 0;
-
- _companies_ainew[c->index].from_ic = -1;
- _companies_ainew[c->index].to_ic = -1;
- if (_companies_ainew[c->index].tbt == AI_BUS) {
- // For now we only have a passenger route
- _companies_ainew[c->index].cargo = CT_PASSENGERS;
-
- // Find a route to cities
- _companies_ainew[c->index].from_type = AI_CITY;
- _companies_ainew[c->index].to_type = AI_CITY;
- } else if (_companies_ainew[c->index].tbt == AI_TRUCK) {
- _companies_ainew[c->index].cargo = AI_NO_CARGO;
-
- _companies_ainew[c->index].from_type = AI_INDUSTRY;
- _companies_ainew[c->index].to_type = AI_INDUSTRY;
- }
-
- // Now we are doing initing, we wait one tick
- return;
- }
-
- // Increase the counter and abort if it is taking too long!
- _companies_ainew[c->index].counter++;
- if (_companies_ainew[c->index].counter > AI_LOCATE_ROUTE_MAX_COUNTER) {
- // Switch back to doing nothing!
- _companies_ainew[c->index].state = AI_STATE_NOTHING;
- return;
- }
-
- // We are going to locate a city from where we are going to connect
- if (_companies_ainew[c->index].from_ic == -1) {
- if (_companies_ainew[c->index].temp == -1) {
- // First, we pick a random spot to search from
- if (_companies_ainew[c->index].from_type == AI_CITY) {
- _companies_ainew[c->index].temp = AI_RandomRange(GetMaxTownIndex() + 1);
- } else {
- _companies_ainew[c->index].temp = AI_RandomRange(GetMaxIndustryIndex() + 1);
- }
- }
-
- if (!AiNew_Check_City_or_Industry(c, _companies_ainew[c->index].temp, _companies_ainew[c->index].from_type)) {
- // It was not a valid city
- // increase the temp with one, and return. We will come back later here
- // to try again
- _companies_ainew[c->index].temp++;
- if (_companies_ainew[c->index].from_type == AI_CITY) {
- if (_companies_ainew[c->index].temp > GetMaxTownIndex()) _companies_ainew[c->index].temp = 0;
- } else {
- if (_companies_ainew[c->index].temp > GetMaxIndustryIndex()) _companies_ainew[c->index].temp = 0;
- }
-
- // Don't do an attempt if we are trying the same id as the last time...
- if (_companies_ainew[c->index].last_id == _companies_ainew[c->index].temp) return;
- _companies_ainew[c->index].last_id = _companies_ainew[c->index].temp;
-
- return;
- }
-
- // We found a good city/industry, save the data of it
- _companies_ainew[c->index].from_ic = _companies_ainew[c->index].temp;
-
- // Start the next tick with finding a to-city
- _companies_ainew[c->index].temp = -1;
- return;
- }
-
- // Find a to-city
- if (_companies_ainew[c->index].temp == -1) {
- // First, we pick a random spot to search to
- if (_companies_ainew[c->index].to_type == AI_CITY) {
- _companies_ainew[c->index].temp = AI_RandomRange(GetMaxTownIndex() + 1);
- } else {
- _companies_ainew[c->index].temp = AI_RandomRange(GetMaxIndustryIndex() + 1);
- }
- }
-
- // The same city is not allowed
- // Also check if the city is valid
- if (_companies_ainew[c->index].temp != _companies_ainew[c->index].from_ic && AiNew_Check_City_or_Industry(c, _companies_ainew[c->index].temp, _companies_ainew[c->index].to_type)) {
- // Maybe it is valid..
-
- /* We need to know if they are not to far apart from eachother..
- * We do that by checking how much cargo we have to move and how long the
- * route is.
- */
-
- if (_companies_ainew[c->index].from_type == AI_CITY && _companies_ainew[c->index].tbt == AI_BUS) {
- const Town *town_from = GetTown(_companies_ainew[c->index].from_ic);
- const Town *town_temp = GetTown(_companies_ainew[c->index].temp);
- uint distance = DistanceManhattan(town_from->xy, town_temp->xy);
- int max_cargo;
-
- max_cargo = town_from->max_pass + town_temp->max_pass;
- max_cargo -= town_from->act_pass + town_temp->act_pass;
-
- // max_cargo is now the amount of cargo we can move between the two cities
- // If it is more than the distance, we allow it
- if (distance <= max_cargo * AI_LOCATEROUTE_BUS_CARGO_DISTANCE) {
- // We found a good city/industry, save the data of it
- _companies_ainew[c->index].to_ic = _companies_ainew[c->index].temp;
- _companies_ainew[c->index].state = AI_STATE_FIND_STATION;
-
- DEBUG(ai, 1, "[LocateRoute] found bus-route of %d tiles long (from %d to %d)",
- distance,
- _companies_ainew[c->index].from_ic,
- _companies_ainew[c->index].temp
- );
-
- _companies_ainew[c->index].from_tile = 0;
- _companies_ainew[c->index].to_tile = 0;
-
- return;
- }
- } else if (_companies_ainew[c->index].tbt == AI_TRUCK) {
- const Industry *ind_from = GetIndustry(_companies_ainew[c->index].from_ic);
- const Industry *ind_temp = GetIndustry(_companies_ainew[c->index].temp);
- bool found = false;
- int max_cargo = 0;
- uint i;
-
- // TODO: in max_cargo, also check other cargo (beside [0])
- // First we check if the from_ic produces cargo that this ic accepts
- if (ind_from->produced_cargo[0] != CT_INVALID && ind_from->last_month_production[0] != 0) {
- for (i = 0; i < lengthof(ind_temp->accepts_cargo); i++) {
- if (ind_temp->accepts_cargo[i] == CT_INVALID) break;
- if (ind_from->produced_cargo[0] == ind_temp->accepts_cargo[i]) {
- // Found a compatible industry
- max_cargo = ind_from->last_month_production[0] - ind_from->last_month_transported[0];
- found = true;
- _companies_ainew[c->index].from_deliver = true;
- _companies_ainew[c->index].to_deliver = false;
- break;
- }
- }
- }
- if (!found && ind_temp->produced_cargo[0] != CT_INVALID && ind_temp->last_month_production[0] != 0) {
- // If not check if the current ic produces cargo that the from_ic accepts
- for (i = 0; i < lengthof(ind_from->accepts_cargo); i++) {
- if (ind_from->accepts_cargo[i] == CT_INVALID) break;
- if (ind_from->produced_cargo[0] == ind_from->accepts_cargo[i]) {
- // Found a compatbiel industry
- found = true;
- max_cargo = ind_temp->last_month_production[0] - ind_temp->last_month_transported[0];
- _companies_ainew[c->index].from_deliver = false;
- _companies_ainew[c->index].to_deliver = true;
- break;
- }
- }
- }
- if (found) {
- // Yeah, they are compatible!!!
- // Check the length against the amount of goods
- uint distance = DistanceManhattan(ind_from->xy, ind_temp->xy);
-
- if (distance > AI_LOCATEROUTE_TRUCK_MIN_DISTANCE &&
- distance <= max_cargo * AI_LOCATEROUTE_TRUCK_CARGO_DISTANCE) {
- _companies_ainew[c->index].to_ic = _companies_ainew[c->index].temp;
- if (_companies_ainew[c->index].from_deliver) {
- _companies_ainew[c->index].cargo = ind_from->produced_cargo[0];
- } else {
- _companies_ainew[c->index].cargo = ind_temp->produced_cargo[0];
- }
- _companies_ainew[c->index].state = AI_STATE_FIND_STATION;
-
- DEBUG(ai, 1, "[LocateRoute] found truck-route of %d tiles long (from %d to %d)",
- distance,
- _companies_ainew[c->index].from_ic,
- _companies_ainew[c->index].temp
- );
-
- _companies_ainew[c->index].from_tile = 0;
- _companies_ainew[c->index].to_tile = 0;
-
- return;
- }
- }
- }
- }
-
- // It was not a valid city
- // increase the temp with one, and return. We will come back later here
- // to try again
- _companies_ainew[c->index].temp++;
- if (_companies_ainew[c->index].to_type == AI_CITY) {
- if (_companies_ainew[c->index].temp > GetMaxTownIndex()) _companies_ainew[c->index].temp = 0;
- } else {
- if (_companies_ainew[c->index].temp > GetMaxIndustryIndex()) _companies_ainew[c->index].temp = 0;
- }
-
- // Don't do an attempt if we are trying the same id as the last time...
- if (_companies_ainew[c->index].last_id == _companies_ainew[c->index].temp) return;
- _companies_ainew[c->index].last_id = _companies_ainew[c->index].temp;
-}
-
-
-// Check if there are not more than a certain amount of vehicles pointed to a certain
-// station. This to prevent 10 busses going to one station, which gives... problems ;)
-static bool AiNew_CheckVehicleStation(Company *c, Station *st)
-{
- int count = 0;
- Vehicle *v;
-
- // Also check if we don't have already a lot of busses to this city...
- FOR_ALL_VEHICLES(v) {
- if (v->owner == _current_company) {
- const Order *order;
-
- FOR_VEHICLE_ORDERS(v, order) {
- if (order->IsType(OT_GOTO_STATION) && GetStation(order->GetDestination()) == st) {
- // This vehicle has this city in its list
- count++;
- }
- }
- }
- }
-
- if (count > AI_CHECK_MAX_VEHICLE_PER_STATION) return false;
- return true;
-}
-
-// This function finds a good spot for a station
-static void AiNew_State_FindStation(Company *c)
-{
- TileIndex tile;
- Station *st;
- int count = 0;
- EngineID i;
- TileIndex new_tile = 0;
- DiagDirection direction = DIAGDIR_NE;
- Town *town = NULL;
- assert(_companies_ainew[c->index].state == AI_STATE_FIND_STATION);
-
- if (_companies_ainew[c->index].from_tile == 0) {
- // First we scan for a station in the from-city
- if (_companies_ainew[c->index].from_type == AI_CITY) {
- town = GetTown(_companies_ainew[c->index].from_ic);
- tile = town->xy;
- } else {
- tile = GetIndustry(_companies_ainew[c->index].from_ic)->xy;
- }
- } else if (_companies_ainew[c->index].to_tile == 0) {
- // Second we scan for a station in the to-city
- if (_companies_ainew[c->index].to_type == AI_CITY) {
- town = GetTown(_companies_ainew[c->index].to_ic);
- tile = town->xy;
- } else {
- tile = GetIndustry(_companies_ainew[c->index].to_ic)->xy;
- }
- } else {
- // Unsupported request
- // Go to FIND_PATH
- _companies_ainew[c->index].temp = -1;
- _companies_ainew[c->index].state = AI_STATE_FIND_PATH;
- return;
- }
-
- // First, we are going to look at the stations that already exist inside the city
- // If there is enough cargo left in the station, we take that station
- // If that is not possible, and there are more than 2 stations in the city, abort
- i = AiNew_PickVehicle(c);
- // Euhmz, this should not happen _EVER_
- // Quit finding a route...
- if (i == INVALID_ENGINE) {
- _companies_ainew[c->index].state = AI_STATE_NOTHING;
- return;
- }
-
- FOR_ALL_STATIONS(st) {
- if (st->owner == _current_company) {
- if (_companies_ainew[c->index].tbt == AI_BUS && (FACIL_BUS_STOP & st->facilities) == FACIL_BUS_STOP) {
- if (st->town == town) {
- // Check how much cargo there is left in the station
- if ((int)st->goods[_companies_ainew[c->index].cargo].cargo.Count() > RoadVehInfo(i)->capacity * AI_STATION_REUSE_MULTIPLER) {
- if (AiNew_CheckVehicleStation(c, st)) {
- // We did found a station that was good enough!
- new_tile = st->xy;
- direction = GetRoadStopDir(st->xy);
- break;
- }
- }
- count++;
- }
- }
- }
- }
- // We are going to add a new station...
- if (new_tile == 0) count++;
- // No more than 2 stations allowed in a city
- // This is because only the best 2 stations of one cargo do get any cargo
- if (count > 2) {
- _companies_ainew[c->index].state = AI_STATE_NOTHING;
- return;
- }
-
- if (new_tile == 0 && _companies_ainew[c->index].tbt == AI_BUS) {
- uint x, y, i = 0;
- CommandCost r;
- uint best;
- uint accepts[NUM_CARGO];
- TileIndex found_spot[AI_FINDSTATION_TILE_RANGE*AI_FINDSTATION_TILE_RANGE * 4];
- uint found_best[AI_FINDSTATION_TILE_RANGE*AI_FINDSTATION_TILE_RANGE * 4];
- // To find a good spot we scan a range from the center, a get the point
- // where we get the most cargo and where it is buildable.
- // TODO: also check for station of myself and make sure we are not
- // taking eachothers passengers away (bad result when it does not)
- for (x = TileX(tile) - AI_FINDSTATION_TILE_RANGE; x <= TileX(tile) + AI_FINDSTATION_TILE_RANGE; x++) {
- for (y = TileY(tile) - AI_FINDSTATION_TILE_RANGE; y <= TileY(tile) + AI_FINDSTATION_TILE_RANGE; y++) {
- new_tile = TileXY(x, y);
- if (IsTileType(new_tile, MP_CLEAR) || IsTileType(new_tile, MP_TREES)) {
- // This tile we can build on!
- // Check acceptance
- // XXX - Get the catchment area
- GetAcceptanceAroundTiles(accepts, new_tile, 1, 1, 4);
- // >> 3 == 0 means no cargo
- if (accepts[_companies_ainew[c->index].cargo] >> 3 == 0) continue;
- // See if we can build the station
- r = AiNew_Build_Station(c, _companies_ainew[c->index].tbt, new_tile, 0, 0, 0, DC_QUERY_COST);
- if (CmdFailed(r)) continue;
- // We can build it, so add it to found_spot
- found_spot[i] = new_tile;
- found_best[i++] = accepts[_companies_ainew[c->index].cargo];
- }
- }
- }
-
- // If i is still zero, we did not find anything
- if (i == 0) {
- _companies_ainew[c->index].state = AI_STATE_NOTHING;
- return;
- }
-
- // Go through all the found_best and check which has the highest value
- best = 0;
- new_tile = 0;
-
- for (x = 0; x < i; x++) {
- if (found_best[x] > best ||
- (found_best[x] == best && DistanceManhattan(tile, new_tile) > DistanceManhattan(tile, found_spot[x]))) {
- new_tile = found_spot[x];
- best = found_best[x];
- }
- }
-
- // See how much it is going to cost us...
- r = AiNew_Build_Station(c, _companies_ainew[c->index].tbt, new_tile, 0, 0, 0, DC_QUERY_COST);
- _companies_ainew[c->index].new_cost += r.GetCost();
-
- direction = (DiagDirection)AI_PATHFINDER_NO_DIRECTION;
- } else if (new_tile == 0 && _companies_ainew[c->index].tbt == AI_TRUCK) {
- // Truck station locater works differently.. a station can be on any place
- // as long as it is in range. So we give back code AI_STATION_RANGE
- // so the pathfinder routine can work it out!
- new_tile = AI_STATION_RANGE;
- direction = (DiagDirection)AI_PATHFINDER_NO_DIRECTION;
- }
-
- if (_companies_ainew[c->index].from_tile == 0) {
- _companies_ainew[c->index].from_tile = new_tile;
- _companies_ainew[c->index].from_direction = direction;
- // Now we found thisone, go in for to_tile
- return;
- } else if (_companies_ainew[c->index].to_tile == 0) {
- _companies_ainew[c->index].to_tile = new_tile;
- _companies_ainew[c->index].to_direction = direction;
- // K, done placing stations!
- _companies_ainew[c->index].temp = -1;
- _companies_ainew[c->index].state = AI_STATE_FIND_PATH;
- return;
- }
-}
-
-
-// We try to find a path between 2 points
-static void AiNew_State_FindPath(Company *c)
-{
- int r;
- assert(_companies_ainew[c->index].state == AI_STATE_FIND_PATH);
-
- // First time, init some data
- if (_companies_ainew[c->index].temp == -1) {
- // Init path_info
- if (_companies_ainew[c->index].from_tile == AI_STATION_RANGE) {
- const Industry *i = GetIndustry(_companies_ainew[c->index].from_ic);
-
- // For truck routes we take a range around the industry
- _companies_ainew[c->index].path_info.start_tile_tl = i->xy - TileDiffXY(1, 1);
- _companies_ainew[c->index].path_info.start_tile_br = i->xy + TileDiffXY(i->width + 1, i->height + 1);
- _companies_ainew[c->index].path_info.start_direction = _companies_ainew[c->index].from_direction;
- } else {
- _companies_ainew[c->index].path_info.start_tile_tl = _companies_ainew[c->index].from_tile;
- _companies_ainew[c->index].path_info.start_tile_br = _companies_ainew[c->index].from_tile;
- _companies_ainew[c->index].path_info.start_direction = _companies_ainew[c->index].from_direction;
- }
-
- if (_companies_ainew[c->index].to_tile == AI_STATION_RANGE) {
- const Industry *i = GetIndustry(_companies_ainew[c->index].to_ic);
-
- _companies_ainew[c->index].path_info.end_tile_tl = i->xy - TileDiffXY(1, 1);
- _companies_ainew[c->index].path_info.end_tile_br = i->xy + TileDiffXY(i->width + 1, i->height + 1);
- _companies_ainew[c->index].path_info.end_direction = _companies_ainew[c->index].to_direction;
- } else {
- _companies_ainew[c->index].path_info.end_tile_tl = _companies_ainew[c->index].to_tile;
- _companies_ainew[c->index].path_info.end_tile_br = _companies_ainew[c->index].to_tile;
- _companies_ainew[c->index].path_info.end_direction = _companies_ainew[c->index].to_direction;
- }
-
- _companies_ainew[c->index].path_info.rail_or_road = (_companies_ainew[c->index].tbt == AI_TRAIN);
-
- // First, clean the pathfinder with our new begin and endpoints
- clean_AyStar_AiPathFinder(_companies_ainew[c->index].pathfinder, &_companies_ainew[c->index].path_info);
-
- _companies_ainew[c->index].temp = 0;
- }
-
- // Start the pathfinder
- r = _companies_ainew[c->index].pathfinder->main(_companies_ainew[c->index].pathfinder);
- switch (r) {
- case AYSTAR_NO_PATH:
- DEBUG(ai, 1, "No route found by pathfinder");
- // Start all over again
- _companies_ainew[c->index].state = AI_STATE_NOTHING;
- break;
-
- case AYSTAR_FOUND_END_NODE: // We found the end-point
- _companies_ainew[c->index].temp = -1;
- _companies_ainew[c->index].state = AI_STATE_FIND_DEPOT;
- break;
-
- // In any other case, we are still busy finding the route
- default: break;
- }
-}
-
-
-// This function tries to locate a good place for a depot!
-static void AiNew_State_FindDepot(Company *c)
-{
- // To place the depot, we walk through the route, and if we find a lovely spot (MP_CLEAR, MP_TREES), we place it there..
- // Simple, easy, works!
- // To make the depot stand in the middle of the route, we start from the center..
- // But first we walk through the route see if we can find a depot that is ours
- // this keeps things nice ;)
- int g, i;
- CommandCost r;
- DiagDirection j;
- TileIndex tile;
- assert(_companies_ainew[c->index].state == AI_STATE_FIND_DEPOT);
-
- _companies_ainew[c->index].depot_tile = 0;
-
- for (i = 2; i < _companies_ainew[c->index].path_info.route_length - 2; i++) {
- tile = _companies_ainew[c->index].path_info.route[i];
- for (j = DIAGDIR_BEGIN; j < DIAGDIR_END; j++) {
- TileIndex t = tile + TileOffsByDiagDir(j);
-
- if (IsRoadDepotTile(t) &&
- IsTileOwner(t, _current_company) &&
- GetRoadDepotDirection(t) == ReverseDiagDir(j)) {
- _companies_ainew[c->index].depot_tile = t;
- _companies_ainew[c->index].depot_direction = ReverseDiagDir(j);
- _companies_ainew[c->index].state = AI_STATE_VERIFY_ROUTE;
- return;
- }
- }
- }
-
- // This routine let depot finding start in the middle, and work his way to the stations
- // It makes depot placing nicer :)
- i = _companies_ainew[c->index].path_info.route_length / 2;
- g = 1;
- while (i > 1 && i < _companies_ainew[c->index].path_info.route_length - 2) {
- i += g;
- g *= -1;
- (g < 0 ? g-- : g++);
-
- if (_companies_ainew[c->index].path_info.route_extra[i] != 0 || _companies_ainew[c->index].path_info.route_extra[i + 1] != 0) {
- // Bridge or tunnel.. we can't place a depot there
- continue;
- }
-
- tile = _companies_ainew[c->index].path_info.route[i];
-
- for (j = DIAGDIR_BEGIN; j < DIAGDIR_END; j++) {
- TileIndex t = tile + TileOffsByDiagDir(j);
-
- // It may not be placed on the road/rail itself
- // And because it is not build yet, we can't see it on the tile..
- // So check the surrounding tiles :)
- if (t == _companies_ainew[c->index].path_info.route[i - 1] ||
- t == _companies_ainew[c->index].path_info.route[i + 1]) {
- continue;
- }
- // Not around a bridge?
- if (_companies_ainew[c->index].path_info.route_extra[i] != 0) continue;
- if (IsTileType(tile, MP_TUNNELBRIDGE)) continue;
- // Is the terrain clear?
- if (IsTileType(t, MP_CLEAR) || IsTileType(t, MP_TREES)) {
- // If the current tile is on a slope then we do not allow this
- if (GetTileSlope(tile, NULL) != SLOPE_FLAT) continue;
- // Check if everything went okay..
- r = AiNew_Build_Depot(c, t, ReverseDiagDir(j), 0);
- if (CmdFailed(r)) continue;
- // Found a spot!
- _companies_ainew[c->index].new_cost += r.GetCost();
- _companies_ainew[c->index].depot_tile = t;
- _companies_ainew[c->index].depot_direction = ReverseDiagDir(j); // Reverse direction
- _companies_ainew[c->index].state = AI_STATE_VERIFY_ROUTE;
- return;
- }
- }
- }
-
- // Failed to find a depot?
- _companies_ainew[c->index].state = AI_STATE_NOTHING;
-}
-
-
-// This function calculates how many vehicles there are needed on this
-// traject.
-// It works pretty simple: get the length, see how much we move around
-// and hussle that, and you know how many vehicles there are needed.
-// It returns the cost for the vehicles
-static int AiNew_HowManyVehicles(Company *c)
-{
- if (_companies_ainew[c->index].tbt == AI_BUS) {
- // For bus-routes we look at the time before we are back in the station
- EngineID i;
- int length, tiles_a_day;
- int amount;
- i = AiNew_PickVehicle(c);
- if (i == INVALID_ENGINE) return 0;
- // Passenger run.. how long is the route?
- length = _companies_ainew[c->index].path_info.route_length;
- // Calculating tiles a day a vehicle moves is not easy.. this is how it must be done!
- tiles_a_day = RoadVehInfo(i)->max_speed * DAY_TICKS / 256 / 16;
- if (tiles_a_day == 0) tiles_a_day = 1;
- // We want a vehicle in a station once a month at least, so, calculate it!
- // (the * 2 is because we have 2 stations ;))
- amount = length * 2 * 2 / tiles_a_day / 30;
- if (amount == 0) amount = 1;
- return amount;
- } else if (_companies_ainew[c->index].tbt == AI_TRUCK) {
- // For truck-routes we look at the cargo
- EngineID i;
- int length, amount, tiles_a_day;
- int max_cargo;
- i = AiNew_PickVehicle(c);
- if (i == INVALID_ENGINE) return 0;
- // Passenger run.. how long is the route?
- length = _companies_ainew[c->index].path_info.route_length;
- // Calculating tiles a day a vehicle moves is not easy.. this is how it must be done!
- tiles_a_day = RoadVehInfo(i)->max_speed * DAY_TICKS / 256 / 16;
- if (tiles_a_day == 0) tiles_a_day = 1;
- if (_companies_ainew[c->index].from_deliver) {
- max_cargo = GetIndustry(_companies_ainew[c->index].from_ic)->last_month_production[0];
- } else {
- max_cargo = GetIndustry(_companies_ainew[c->index].to_ic)->last_month_production[0];
- }
-
- // This is because moving 60% is more than we can dream of!
- max_cargo *= 6;
- max_cargo /= 10;
- // We want all the cargo to be gone in a month.. so, we know the cargo it delivers
- // we know what the vehicle takes with him, and we know the time it takes him
- // to get back here.. now let's do some math!
- amount = 2 * length * max_cargo / tiles_a_day / 30 / RoadVehInfo(i)->capacity;
- amount += 1;
- return amount;
- } else {
- // Currently not supported
- return 0;
- }
-}
-
-
-// This function checks:
-// - If the route went okay
-// - Calculates the amount of money needed to build the route
-// - Calculates how much vehicles needed for the route
-static void AiNew_State_VerifyRoute(Company *c)
-{
- int res, i;
- assert(_companies_ainew[c->index].state == AI_STATE_VERIFY_ROUTE);
-
- // Let's calculate the cost of the path..
- // new_cost already contains the cost of the stations
- _companies_ainew[c->index].path_info.position = -1;
-
- do {
- _companies_ainew[c->index].path_info.position++;
- _companies_ainew[c->index].new_cost += AiNew_Build_RoutePart(c, &_companies_ainew[c->index].path_info, DC_QUERY_COST).GetCost();
- } while (_companies_ainew[c->index].path_info.position != -2);
-
- // Now we know the price of build station + path. Now check how many vehicles
- // we need and what the price for that will be
- res = AiNew_HowManyVehicles(c);
- // If res == 0, no vehicle was found, or an other problem did occour
- if (res == 0) {
- _companies_ainew[c->index].state = AI_STATE_NOTHING;
- return;
- }
- _companies_ainew[c->index].amount_veh = res;
- _companies_ainew[c->index].cur_veh = 0;
-
- // Check how much it it going to cost us..
- for (i = 0; i < res; i++) {
- _companies_ainew[c->index].new_cost += AiNew_Build_Vehicle(c, 0, DC_QUERY_COST).GetCost();
- }
-
- // Now we know how much the route is going to cost us
- // Check if we have enough money for it!
- if (_companies_ainew[c->index].new_cost > c->money - AI_MINIMUM_MONEY) {
- // Too bad..
- DEBUG(ai, 1, "Insufficient funds to build route (%" OTTD_PRINTF64 "d)", (int64)_companies_ainew[c->index].new_cost);
- _companies_ainew[c->index].state = AI_STATE_NOTHING;
- return;
- }
-
- // Now we can build the route, check the direction of the stations!
- if (_companies_ainew[c->index].from_direction == AI_PATHFINDER_NO_DIRECTION) {
- _companies_ainew[c->index].from_direction = AiNew_GetDirection(_companies_ainew[c->index].path_info.route[_companies_ainew[c->index].path_info.route_length - 1], _companies_ainew[c->index].path_info.route[_companies_ainew[c->index].path_info.route_length - 2]);
- }
- if (_companies_ainew[c->index].to_direction == AI_PATHFINDER_NO_DIRECTION) {
- _companies_ainew[c->index].to_direction = AiNew_GetDirection(_companies_ainew[c->index].path_info.route[0], _companies_ainew[c->index].path_info.route[1]);
- }
- if (_companies_ainew[c->index].from_tile == AI_STATION_RANGE)
- _companies_ainew[c->index].from_tile = _companies_ainew[c->index].path_info.route[_companies_ainew[c->index].path_info.route_length - 1];
- if (_companies_ainew[c->index].to_tile == AI_STATION_RANGE)
- _companies_ainew[c->index].to_tile = _companies_ainew[c->index].path_info.route[0];
-
- _companies_ainew[c->index].state = AI_STATE_BUILD_STATION;
- _companies_ainew[c->index].temp = 0;
-
- DEBUG(ai, 1, "The route is set and buildable, building 0x%X to 0x%X...", _companies_ainew[c->index].from_tile, _companies_ainew[c->index].to_tile);
-}
-
-
-// Build the stations
-static void AiNew_State_BuildStation(Company *c)
-{
- CommandCost res;
- assert(_companies_ainew[c->index].state == AI_STATE_BUILD_STATION);
- if (_companies_ainew[c->index].temp == 0) {
- if (!IsTileType(_companies_ainew[c->index].from_tile, MP_STATION))
- res = AiNew_Build_Station(c, _companies_ainew[c->index].tbt, _companies_ainew[c->index].from_tile, 0, 0, _companies_ainew[c->index].from_direction, DC_EXEC);
- } else {
- if (!IsTileType(_companies_ainew[c->index].to_tile, MP_STATION))
- res = AiNew_Build_Station(c, _companies_ainew[c->index].tbt, _companies_ainew[c->index].to_tile, 0, 0, _companies_ainew[c->index].to_direction, DC_EXEC);
- _companies_ainew[c->index].state = AI_STATE_BUILD_PATH;
- }
- if (CmdFailed(res)) {
- DEBUG(ai, 0, "[BuildStation] station could not be built (0x%X)", _companies_ainew[c->index].to_tile);
- _companies_ainew[c->index].state = AI_STATE_NOTHING;
- // If the first station _was_ build, destroy it
- if (_companies_ainew[c->index].temp != 0)
- AI_DoCommand(_companies_ainew[c->index].from_tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
- return;
- }
- _companies_ainew[c->index].temp++;
-}
-
-
-// Build the path
-static void AiNew_State_BuildPath(Company *c)
-{
- assert(_companies_ainew[c->index].state == AI_STATE_BUILD_PATH);
- // _companies_ainew[c->index].temp is set to -1 when this function is called for the first time
- if (_companies_ainew[c->index].temp == -1) {
- DEBUG(ai, 1, "Starting to build new path");
- // Init the counter
- _companies_ainew[c->index].counter = (4 - _settings_game.difficulty.competitor_speed) * AI_BUILDPATH_PAUSE + 1;
- // Set the position to the startingplace (-1 because in a minute we do ++)
- _companies_ainew[c->index].path_info.position = -1;
- // And don't do this again
- _companies_ainew[c->index].temp = 0;
- }
- // Building goes very fast on normal rate, so we are going to slow it down..
- // By let the counter count from AI_BUILDPATH_PAUSE to 0, we have a nice way :)
- if (--_companies_ainew[c->index].counter != 0) return;
- _companies_ainew[c->index].counter = (4 - _settings_game.difficulty.competitor_speed) * AI_BUILDPATH_PAUSE + 1;
-
- // Increase the building position
- _companies_ainew[c->index].path_info.position++;
- // Build route
- AiNew_Build_RoutePart(c, &_companies_ainew[c->index].path_info, DC_EXEC);
- if (_companies_ainew[c->index].path_info.position == -2) {
- // This means we are done building!
-
- if (_companies_ainew[c->index].tbt == AI_TRUCK && !_settings_game.pf.roadveh_queue) {
- // If they not queue, they have to go up and down to try again at a station...
- // We don't want that, so try building some road left or right of the station
- DiagDirection dir1, dir2, dir3;
- TileIndex tile;
- CommandCost ret;
- for (int i = 0; i < 2; i++) {
- if (i == 0) {
- tile = _companies_ainew[c->index].from_tile + TileOffsByDiagDir(_companies_ainew[c->index].from_direction);
- dir1 = ChangeDiagDir(_companies_ainew[c->index].from_direction, DIAGDIRDIFF_90LEFT);
- dir2 = ChangeDiagDir(_companies_ainew[c->index].from_direction, DIAGDIRDIFF_90RIGHT);
- dir3 = _companies_ainew[c->index].from_direction;
- } else {
- tile = _companies_ainew[c->index].to_tile + TileOffsByDiagDir(_companies_ainew[c->index].to_direction);
- dir1 = ChangeDiagDir(_companies_ainew[c->index].to_direction, DIAGDIRDIFF_90LEFT);
- dir2 = ChangeDiagDir(_companies_ainew[c->index].to_direction, DIAGDIRDIFF_90RIGHT);
- dir3 = _companies_ainew[c->index].to_direction;
- }
-
- ret = AI_DoCommand(tile, DiagDirToRoadBits(ReverseDiagDir(dir1)), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
- if (CmdSucceeded(ret)) {
- TileIndexDiff offset = TileOffsByDiagDir(dir1);
- if (IsTileType(tile + offset, MP_CLEAR) || IsTileType(tile + offset, MP_TREES)) {
- ret = AI_DoCommand(tile + offset, AiNew_GetRoadDirection(tile, tile + offset, tile + offset + offset), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
- if (CmdSucceeded(ret)) {
- if (IsTileType(tile + offset + offset, MP_CLEAR) || IsTileType(tile + offset + offset, MP_TREES))
- AI_DoCommand(tile + offset + offset, AiNew_GetRoadDirection(tile + offset, tile + offset + offset, tile + offset + offset + offset), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
- }
- }
- }
-
- ret = AI_DoCommand(tile, DiagDirToRoadBits(ReverseDiagDir(dir2)), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
- if (CmdSucceeded(ret)) {
- TileIndexDiff offset = TileOffsByDiagDir(dir2);
- if (IsTileType(tile + offset, MP_CLEAR) || IsTileType(tile + offset, MP_TREES)) {
- ret = AI_DoCommand(tile + offset, AiNew_GetRoadDirection(tile, tile + offset, tile + offset + offset), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
- if (CmdSucceeded(ret)) {
- if (IsTileType(tile + offset + offset, MP_CLEAR) || IsTileType(tile + offset + offset, MP_TREES))
- AI_DoCommand(tile + offset + offset, AiNew_GetRoadDirection(tile + offset, tile + offset + offset, tile + offset + offset + offset), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
- }
- }
- }
-
- ret = AI_DoCommand(tile, DiagDirToRoadBits(dir3), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
- if (CmdSucceeded(ret)) {
- TileIndexDiff offset = TileOffsByDiagDir(dir3);
- if (IsTileType(tile + offset, MP_CLEAR) || IsTileType(tile + offset, MP_TREES)) {
- ret = AI_DoCommand(tile + offset, AiNew_GetRoadDirection(tile, tile + offset, tile + offset + offset), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
- if (CmdSucceeded(ret)) {
- if (IsTileType(tile + offset + offset, MP_CLEAR) || IsTileType(tile + offset + offset, MP_TREES))
- AI_DoCommand(tile + offset + offset, AiNew_GetRoadDirection(tile + offset, tile + offset + offset, tile + offset + offset + offset), 0, DC_EXEC | DC_NO_WATER, CMD_BUILD_ROAD);
- }
- }
- }
- }
- }
-
- DEBUG(ai, 1, "Finished building path, cost: %" OTTD_PRINTF64 "d", (int64)_companies_ainew[c->index].new_cost);
- _companies_ainew[c->index].state = AI_STATE_BUILD_DEPOT;
- }
-}
-
-
-// Builds the depot
-static void AiNew_State_BuildDepot(Company *c)
-{
- CommandCost res;
- assert(_companies_ainew[c->index].state == AI_STATE_BUILD_DEPOT);
-
- if (IsRoadDepotTile(_companies_ainew[c->index].depot_tile)) {
- if (IsTileOwner(_companies_ainew[c->index].depot_tile, _current_company)) {
- // The depot is already built
- _companies_ainew[c->index].state = AI_STATE_BUILD_VEHICLE;
- return;
- } else {
- // There is a depot, but not of our team! :(
- _companies_ainew[c->index].state = AI_STATE_NOTHING;
- return;
- }
- }
-
- // There is a bus on the tile we want to build road on... idle till he is gone! (BAD PERSON! :p)
- if (!EnsureNoVehicleOnGround(_companies_ainew[c->index].depot_tile + TileOffsByDiagDir(_companies_ainew[c->index].depot_direction)))
- return;
-
- res = AiNew_Build_Depot(c, _companies_ainew[c->index].depot_tile, _companies_ainew[c->index].depot_direction, DC_EXEC);
- if (CmdFailed(res)) {
- DEBUG(ai, 0, "[BuildDepot] depot could not be built (0x%X)", _companies_ainew[c->index].depot_tile);
- _companies_ainew[c->index].state = AI_STATE_NOTHING;
- return;
- }
-
- _companies_ainew[c->index].state = AI_STATE_BUILD_VEHICLE;
- _companies_ainew[c->index].idle = 10;
- _companies_ainew[c->index].veh_main_id = INVALID_VEHICLE;
-}
-
-
-// Build vehicles
-static void AiNew_State_BuildVehicle(Company *c)
-{
- CommandCost res;
- assert(_companies_ainew[c->index].state == AI_STATE_BUILD_VEHICLE);
-
- // Check if we need to build a vehicle
- if (_companies_ainew[c->index].amount_veh == 0) {
- // Nope, we are done!
- // This means: we are all done! The route is open.. go back to NOTHING
- // He will idle some time and it will all start over again.. :)
- _companies_ainew[c->index].state = AI_STATE_ACTION_DONE;
- return;
- }
- if (--_companies_ainew[c->index].idle != 0) return;
- // It is realistic that the AI can only build 1 vehicle a day..
- // This makes sure of that!
- _companies_ainew[c->index].idle = AI_BUILD_VEHICLE_TIME_BETWEEN;
-
- // Build the vehicle
- res = AiNew_Build_Vehicle(c, _companies_ainew[c->index].depot_tile, DC_EXEC);
- if (CmdFailed(res)) {
- // This happens when the AI can't build any more vehicles!
- _companies_ainew[c->index].state = AI_STATE_NOTHING;
- return;
- }
- // Increase the current counter
- _companies_ainew[c->index].cur_veh++;
- // Decrease the total counter
- _companies_ainew[c->index].amount_veh--;
- // Go give some orders!
- _companies_ainew[c->index].state = AI_STATE_WAIT_FOR_BUILD;
-}
-
-
-// Put the stations in the order list
-static void AiNew_State_GiveOrders(Company *c)
-{
- int idx;
- Order order;
-
- assert(_companies_ainew[c->index].state == AI_STATE_GIVE_ORDERS);
-
- if (_companies_ainew[c->index].veh_main_id != INVALID_VEHICLE) {
- AI_DoCommand(0, _companies_ainew[c->index].veh_id + (_companies_ainew[c->index].veh_main_id << 16), CO_SHARE, DC_EXEC, CMD_CLONE_ORDER);
-
- _companies_ainew[c->index].state = AI_STATE_START_VEHICLE;
- return;
- } else {
- _companies_ainew[c->index].veh_main_id = _companies_ainew[c->index].veh_id;
- }
-
- // Very handy for AI, goto depot.. but yeah, it needs to be activated ;)
- if (_settings_game.order.gotodepot) {
- idx = 0;
- order.MakeGoToDepot(GetDepotByTile(_companies_ainew[c->index].depot_tile)->index, ODTFB_PART_OF_ORDERS);
- AI_DoCommand(0, _companies_ainew[c->index].veh_id + (idx << 16), order.Pack(), DC_EXEC, CMD_INSERT_ORDER);
- }
-
- idx = 0;
- order.MakeGoToStation(GetStationIndex(_companies_ainew[c->index].to_tile));
- if (_companies_ainew[c->index].tbt == AI_TRUCK && _companies_ainew[c->index].to_deliver) order.SetLoadType(OLFB_FULL_LOAD);
- AI_DoCommand(0, _companies_ainew[c->index].veh_id + (idx << 16), order.Pack(), DC_EXEC, CMD_INSERT_ORDER);
-
- idx = 0;
- order.MakeGoToStation(GetStationIndex(_companies_ainew[c->index].from_tile));
- if (_companies_ainew[c->index].tbt == AI_TRUCK && _companies_ainew[c->index].from_deliver) order.SetLoadType(OLFB_FULL_LOAD);
- AI_DoCommand(0, _companies_ainew[c->index].veh_id + (idx << 16), order.Pack(), DC_EXEC, CMD_INSERT_ORDER);
-
- // Start the engines!
- _companies_ainew[c->index].state = AI_STATE_START_VEHICLE;
-}
-
-
-// Start the vehicle
-static void AiNew_State_StartVehicle(Company *c)
-{
- assert(_companies_ainew[c->index].state == AI_STATE_START_VEHICLE);
-
- // Skip the first order if it is a second vehicle
- // This to make vehicles go different ways..
- if (_companies_ainew[c->index].cur_veh & 1)
- AI_DoCommand(0, _companies_ainew[c->index].veh_id, 1, DC_EXEC, CMD_SKIP_TO_ORDER);
-
- // 3, 2, 1... go! (give START_STOP command ;))
- AI_DoCommand(0, _companies_ainew[c->index].veh_id, 0, DC_EXEC, CMD_START_STOP_VEHICLE);
- // Try to build an other vehicle (that function will stop building when needed)
- _companies_ainew[c->index].idle = 10;
- _companies_ainew[c->index].state = AI_STATE_BUILD_VEHICLE;
-}
-
-
-// Repays money
-static void AiNew_State_RepayMoney(Company *c)
-{
- uint i;
-
- for (i = 0; i < AI_LOAN_REPAY; i++) {
- AI_DoCommand(0, 0, 0, DC_EXEC, CMD_DECREASE_LOAN);
- }
- _companies_ainew[c->index].state = AI_STATE_ACTION_DONE;
-}
-
-
-static void AiNew_CheckVehicle(Company *c, Vehicle *v)
-{
- // When a vehicle is under the 6 months, we don't check for anything
- if (v->age < 180) return;
-
- // When a vehicle is older than 1 year, it should make money...
- if (v->age > 360) {
- // If both years together are not more than AI_MINIMUM_ROUTE_PROFIT,
- // it is not worth the line I guess...
- if (v->profit_last_year + v->profit_this_year < (Money)256 * AI_MINIMUM_ROUTE_PROFIT ||
- (v->reliability * 100 >> 16) < 40) {
- // There is a possibility that the route is fucked up...
- if (v->cargo.DaysInTransit() > AI_VEHICLE_LOST_DAYS) {
- // The vehicle is lost.. check the route, or else, get the vehicle
- // back to a depot
- // TODO: make this piece of code
- }
-
-
- // We are already sending him back
- if (AiNew_GetSpecialVehicleFlag(c, v) & AI_VEHICLEFLAG_SELL) {
- if (v->type == VEH_ROAD && IsRoadDepotTile(v->tile) &&
- (v->vehstatus & VS_STOPPED)) {
- // We are at the depot, sell the vehicle
- AI_DoCommand(0, v->index, 0, DC_EXEC, CMD_SELL_ROAD_VEH);
- }
- return;
- }
-
- if (!AiNew_SetSpecialVehicleFlag(c, v, AI_VEHICLEFLAG_SELL)) return;
- {
- CommandCost ret;
- if (v->type == VEH_ROAD)
- ret = AI_DoCommand(0, v->index, 0, DC_EXEC, CMD_SEND_ROADVEH_TO_DEPOT);
- // This means we can not find a depot :s
- // if (CmdFailed(ret))
- }
- }
- }
-}
-
-
-// Checks all vehicles if they are still valid and make money and stuff
-static void AiNew_State_CheckAllVehicles(Company *c)
-{
- Vehicle *v;
-
- FOR_ALL_VEHICLES(v) {
- if (v->owner != c->index) continue;
- // Currently, we only know how to handle road-vehicles
- if (v->type != VEH_ROAD) continue;
-
- AiNew_CheckVehicle(c, v);
- }
-
- _companies_ainew[c->index].state = AI_STATE_ACTION_DONE;
-}
-
-
-// Using the technique simular to the original AI
-// Keeps things logical
-// It really should be in the same order as the AI_STATE's are!
-static AiNew_StateFunction * const _ainew_state[] = {
- NULL,
- AiNew_State_FirstTime,
- AiNew_State_Nothing,
- AiNew_State_WakeUp,
- AiNew_State_LocateRoute,
- AiNew_State_FindStation,
- AiNew_State_FindPath,
- AiNew_State_FindDepot,
- AiNew_State_VerifyRoute,
- AiNew_State_BuildStation,
- AiNew_State_BuildPath,
- AiNew_State_BuildDepot,
- AiNew_State_BuildVehicle,
- NULL,
- AiNew_State_GiveOrders,
- AiNew_State_StartVehicle,
- AiNew_State_RepayMoney,
- AiNew_State_CheckAllVehicles,
- AiNew_State_ActionDone,
- NULL,
-};
-
-static void AiNew_OnTick(Company *c)
-{
- if (_ainew_state[_companies_ainew[c->index].state] != NULL)
- _ainew_state[_companies_ainew[c->index].state](c);
-}
-
-
-void AiNewDoGameLoop(Company *c)
-{
- if (_companies_ainew[c->index].state == AI_STATE_STARTUP) {
- // The AI just got alive!
- _companies_ainew[c->index].state = AI_STATE_FIRST_TIME;
- _companies_ainew[c->index].tick = 0;
-
- // Only startup the AI
- return;
- }
-
- // We keep a ticker. We use it for competitor_speed
- _companies_ainew[c->index].tick++;
-
- // If we come here, we can do a tick.. do so!
- AiNew_OnTick(c);
-}
diff --git a/src/ai/trolly/trolly.h b/src/ai/trolly/trolly.h
deleted file mode 100644
index 60180694c..000000000
--- a/src/ai/trolly/trolly.h
+++ /dev/null
@@ -1,344 +0,0 @@
-/* $Id$ */
-
-/** @file trolly.h Functions/defines related to the trolly AI. */
-
-#ifndef AI_TROLLY_H
-#define AI_TROLLY_H
-
-#include "../../aystar.h"
-#include "../../company_type.h"
-#include "../../vehicle_type.h"
-#include "../../date_type.h"
-#include "../../engine_type.h"
-#include "../../direction_type.h"
-
-/*
- * These defines can be altered to change the behavoir of the AI
- *
- * WARNING:
- * This can also alter the AI in a negative way. I will never claim these settings
- * are perfect, but don't change them if you don't know what the effect is.
- */
-
-// How many times it the H multiplied. The higher, the more it will go straight to the
-// end point. The lower, how more it will find the route with the lowest cost.
-// also: the lower, the longer it takes before route is calculated..
-#define AI_PATHFINDER_H_MULTIPLER 100
-
-// How many loops may AyStar do before it stops
-// 0 = infinite
-#define AI_PATHFINDER_LOOPS_PER_TICK 5
-
-// How long may the AI search for one route?
-// 0 = infinite
-// This number is the number of tiles tested.
-// It takes (AI_PATHFINDER_MAX_SEARCH_NODES / AI_PATHFINDER_LOOPS_PER_TICK) ticks
-// to get here.. with 5000 / 10 = 500. 500 / 74 (one day) = 8 days till it aborts
-// (that is: if the AI is on VERY FAST! :p
-#define AI_PATHFINDER_MAX_SEARCH_NODES 5000
-
-// If you enable this, the AI is not allowed to make 90degree turns
-#define AI_PATHFINDER_NO_90DEGREES_TURN
-
-// Below are defines for the g-calculation
-
-// Standard penalty given to a tile
-#define AI_PATHFINDER_PENALTY 150
-// The penalty given to a tile that is going up
-#define AI_PATHFINDER_TILE_GOES_UP_PENALTY 450
-// The penalty given to a tile which would have to use fundation
-#define AI_PATHFINDER_FOUNDATION_PENALTY 100
-// Changing direction is a penalty, to prevent curved ways (with that: slow ways)
-#define AI_PATHFINDER_DIRECTION_CHANGE_PENALTY 200
-// Same penalty, only for when road already exists
-#define AI_PATHFINDER_DIRECTION_CHANGE_ON_EXISTING_ROAD_PENALTY 50
-// A diagonal track cost the same as a straigh, but a diagonal is faster... so give
-// a bonus for using diagonal track
-#ifdef AI_PATHFINDER_NO_90DEGREES_TURN
-#define AI_PATHFINDER_DIAGONAL_BONUS 95
-#else
-#define AI_PATHFINDER_DIAGONAL_BONUS 75
-#endif
-// If a roadblock already exists, it gets a bonus
-#define AI_PATHFINDER_ROAD_ALREADY_EXISTS_BONUS 140
-// To prevent 3 direction changes in 3 tiles, this penalty is given in such situation
-#define AI_PATHFINDER_CURVE_PENALTY 200
-
-// Penalty a bridge gets per length
-#define AI_PATHFINDER_BRIDGE_PENALTY 180
-// The penalty for a bridge going up
-#define AI_PATHFINDER_BRIDGE_GOES_UP_PENALTY 1000
-
-// Tunnels are expensive...
-// Because of that, every tile the cost is increased with 1/8th of his value
-// This is also true if you are building a tunnel yourself
-#define AI_PATHFINDER_TUNNEL_PENALTY 350
-
-/*
- * Ai_New defines
- */
-
-// How long may we search cities and industry for a new route?
-#define AI_LOCATE_ROUTE_MAX_COUNTER 200
-
-// How many days must there be between building the first station and the second station
-// within one city. This number is in days and should be more than 4 months.
-#define AI_CHECKCITY_DATE_BETWEEN 180
-
-// How many cargo is needed for one station in a city?
-#define AI_CHECKCITY_CARGO_PER_STATION 60
-// How much cargo must there not be used in a city before we can build a new station?
-#define AI_CHECKCITY_NEEDED_CARGO 50
-// When there is already a station which takes the same good and the rating of that
-// city is higher than this numer, we are not going to attempt to build anything
-// there
-#define AI_CHECKCITY_CARGO_RATING 50
-// But, there is a chance of 1 out of this number, that we do ;)
-#define AI_CHECKCITY_CARGO_RATING_CHANCE 5
-// If a city is too small to contain a station, there is a small chance
-// that we still do so.. just to make the city bigger!
-#define AI_CHECKCITY_CITY_CHANCE 5
-
-// This number indicates for every unit of cargo, how many tiles two stations maybe be away
-// from eachother. In other words: if we have 120 units of cargo in one station, and 120 units
-// of the cargo in the other station, both stations can be 96 units away from eachother, if the
-// next number is 0.4.
-#define AI_LOCATEROUTE_BUS_CARGO_DISTANCE 0.4
-#define AI_LOCATEROUTE_TRUCK_CARGO_DISTANCE 0.7
-// In whole tiles, the minimum distance for a truck route
-#define AI_LOCATEROUTE_TRUCK_MIN_DISTANCE 30
-
-// The amount of tiles in a square from -X to +X that is scanned for a station spot
-// (so if this number is 10, 20x20 = 400 tiles are scanned for _the_ perfect spot
-// Safe values are between 15 and 5
-#define AI_FINDSTATION_TILE_RANGE 10
-
-// Building on normal speed goes very fast. Idle this amount of ticks between every
-// building part. It is calculated like this: (4 - competitor_speed) * num + 1
-// where competitor_speed is between 0 (very slow) to 4 (very fast)
-#define AI_BUILDPATH_PAUSE 10
-
-// Minimum % of reliabilty a vehicle has to have before the AI buys it
-#define AI_VEHICLE_MIN_RELIABILTY 60
-
-// The minimum amount of money a company should always have
-#define AI_MINIMUM_MONEY 15000
-
-// If the most cheap route is build, how much is it going to cost..
-// This is to prevent the AI from trying to build a route which can not be paid for
-#define AI_MINIMUM_BUS_ROUTE_MONEY 25000
-#define AI_MINIMUM_TRUCK_ROUTE_MONEY 35000
-
-// The minimum amount of money before we are going to repay any money
-#define AI_MINIMUM_LOAN_REPAY_MONEY 40000
-// How many repays do we do if we have enough money to do so?
-// Every repay is 10000
-#define AI_LOAN_REPAY 2
-// How much income must we have before paying back a loan? Month-based (and looked at the last month)
-#define AI_MINIMUM_INCOME_FOR_LOAN 7000
-
-// If there is <num> time as much cargo in the station then the vehicle can handle
-// reuse the station instead of building a new one!
-#define AI_STATION_REUSE_MULTIPLER 2
-
-// No more than this amount of vehicles per station..
-#define AI_CHECK_MAX_VEHICLE_PER_STATION 10
-
-// How many thick between building 2 vehicles
-#define AI_BUILD_VEHICLE_TIME_BETWEEN DAY_TICKS
-
-// How many days must there between vehicle checks
-// The more often, the less non-money-making lines there will be
-// but the unfair it may seem to a human company
-#define AI_DAYS_BETWEEN_VEHICLE_CHECKS 30
-
-// How money profit does a vehicle needs to make to stay in order
-// This is the profit of this year + profit of last year
-// But also for vehicles that are just one year old. In other words:
-// Vehicles of 2 years do easier meet this setting than vehicles
-// of one year. This is a very good thing. New vehicles are filtered,
-// while old vehicles stay longer, because we do get less in return.
-#define AI_MINIMUM_ROUTE_PROFIT 1000
-
-// A vehicle is considered lost when he his cargo is more than 180 days old
-#define AI_VEHICLE_LOST_DAYS 180
-
-// How many times may the AI try to find a route before it gives up
-#define AI_MAX_TRIES_FOR_SAME_ROUTE 8
-
-/*
- * End of defines
- */
-
-// This stops 90degrees curves
-static const byte _illegal_curves[6] = {
- 255, 255, // Horz and vert, don't have the effect
- 5, // upleft and upright are not valid
- 4, // downright and downleft are not valid
- 2, // downleft and upleft are not valid
- 3, // upright and downright are not valid
-};
-
-enum {
- AI_STATE_STARTUP = 0,
- AI_STATE_FIRST_TIME,
- AI_STATE_NOTHING,
- AI_STATE_WAKE_UP,
- AI_STATE_LOCATE_ROUTE,
- AI_STATE_FIND_STATION,
- AI_STATE_FIND_PATH,
- AI_STATE_FIND_DEPOT,
- AI_STATE_VERIFY_ROUTE,
- AI_STATE_BUILD_STATION,
- AI_STATE_BUILD_PATH,
- AI_STATE_BUILD_DEPOT,
- AI_STATE_BUILD_VEHICLE,
- AI_STATE_WAIT_FOR_BUILD,
- AI_STATE_GIVE_ORDERS,
- AI_STATE_START_VEHICLE,
- AI_STATE_REPAY_MONEY,
- AI_STATE_CHECK_ALL_VEHICLES,
- AI_STATE_ACTION_DONE,
- AI_STATE_STOP, // Temporary function to stop the AI
-};
-
-// Used for tbt (train/bus/truck)
-enum {
- AI_TRAIN = 0,
- AI_BUS,
- AI_TRUCK,
-};
-
-enum {
- AI_ACTION_NONE = 0,
- AI_ACTION_BUS_ROUTE,
- AI_ACTION_TRUCK_ROUTE,
- AI_ACTION_REPAY_LOAN,
- AI_ACTION_CHECK_ALL_VEHICLES,
-};
-
-// Used for from_type/to_type
-enum {
- AI_NO_TYPE = 0,
- AI_CITY,
- AI_INDUSTRY,
-};
-
-// Flags for in the vehicle
-enum {
- AI_VEHICLEFLAG_SELL = 1,
- // Remember, flags must be in power of 2
-};
-
-#define AI_NO_CARGO 0xFF // Means that there is no cargo defined yet (used for industry)
-#define AI_NEED_CARGO 0xFE // Used when the AI needs to find out a cargo for the route
-#define AI_STATION_RANGE TileXY(MapMaxX(), MapMaxY())
-
-#define AI_PATHFINDER_NO_DIRECTION (byte)-1
-
-// Flags used in user_data
-#define AI_PATHFINDER_FLAG_BRIDGE 1
-#define AI_PATHFINDER_FLAG_TUNNEL 2
-
-typedef void AiNew_StateFunction(Company *c);
-
-// ai_new.c
-void AiNewDoGameLoop(Company *c);
-
-struct Ai_PathFinderInfo {
- TileIndex start_tile_tl; ///< tl = top-left
- TileIndex start_tile_br; ///< br = bottom-right
- TileIndex end_tile_tl; ///< tl = top-left
- TileIndex end_tile_br; ///< br = bottom-right
- DiagDirection start_direction; ///< 0 to 3 or AI_PATHFINDER_NO_DIRECTION
- DiagDirection end_direction; ///< 0 to 3 or AI_PATHFINDER_NO_DIRECTION
-
- TileIndex route[500];
- byte route_extra[500]; ///< Some extra information about the route like bridge/tunnel
- int route_length;
- int position; ///< Current position in the build-path, needed to build the path
-
- bool rail_or_road; ///< true = rail, false = road
-};
-
-// ai_pathfinder.c
-AyStar *new_AyStar_AiPathFinder(int max_tiles_around, Ai_PathFinderInfo *PathFinderInfo);
-void clean_AyStar_AiPathFinder(AyStar *aystar, Ai_PathFinderInfo *PathFinderInfo);
-
-// ai_shared.c
-int AiNew_GetRailDirection(TileIndex tile_a, TileIndex tile_b, TileIndex tile_c);
-int AiNew_GetRoadDirection(TileIndex tile_a, TileIndex tile_b, TileIndex tile_c);
-DiagDirection AiNew_GetDirection(TileIndex tile_a, TileIndex tile_b);
-bool AiNew_SetSpecialVehicleFlag(Company *c, Vehicle *v, uint flag);
-uint AiNew_GetSpecialVehicleFlag(Company *c, Vehicle *v);
-
-// ai_build.c
-bool AiNew_Build_CompanyHQ(Company *c, TileIndex tile);
-CommandCost AiNew_Build_Station(Company *c, byte type, TileIndex tile, byte length, byte numtracks, byte direction, byte flag);
-CommandCost AiNew_Build_Bridge(Company *c, TileIndex tile_a, TileIndex tile_b, byte flag);
-CommandCost AiNew_Build_RoutePart(Company *c, Ai_PathFinderInfo *PathFinderInfo, byte flag);
-EngineID AiNew_PickVehicle(Company *c);
-CommandCost AiNew_Build_Vehicle(Company *c, TileIndex tile, byte flag);
-CommandCost AiNew_Build_Depot(Company *c, TileIndex tile, DiagDirection direction, byte flag);
-
-/* The amount of memory reserved for the AI-special-vehicles */
-#define AI_MAX_SPECIAL_VEHICLES 100
-
-struct Ai_SpecialVehicle {
- VehicleID veh_id;
- uint32 flag;
-};
-
-struct CompanyAiNew {
- uint8 state;
- uint tick;
- uint idle;
-
- int temp; ///< A value used in more than one function, but it just temporary
- ///< The use is pretty simple: with this we can 'think' about stuff
- ///< in more than one tick, and more than one AI. A static will not
- ///< do, because they are not saved. This way, the AI is almost human ;)
- int counter; ///< For the same reason as temp, we have counter. It can count how
- ///< long we are trying something, and just abort if it takes too long
-
- /* Pathfinder stuff */
- Ai_PathFinderInfo path_info;
- AyStar *pathfinder;
-
- /* Route stuff */
-
- CargoID cargo;
- byte tbt; ///< train/bus/truck 0/1/2 AI_TRAIN/AI_BUS/AI_TRUCK
- Money new_cost;
-
- byte action;
-
- int last_id; ///< here is stored the last id of the searched city/industry
- Date last_vehiclecheck_date; // Used in CheckVehicle
- Ai_SpecialVehicle special_vehicles[AI_MAX_SPECIAL_VEHICLES]; ///< Some vehicles have some special flags
-
- TileIndex from_tile;
- TileIndex to_tile;
-
- DiagDirectionByte from_direction;
- DiagDirectionByte to_direction;
-
- bool from_deliver; ///< True if this is the station that GIVES cargo
- bool to_deliver;
-
- TileIndex depot_tile;
- DiagDirectionByte depot_direction;
-
- byte amount_veh; ///< How many vehicles we are going to build in this route
- byte cur_veh; ///< How many vehicles did we bought?
- VehicleID veh_id; ///< Used when bought a vehicle
- VehicleID veh_main_id; ///< The ID of the first vehicle, for shared copy
-
- int from_ic; ///< ic = industry/city. This is the ID of them
- byte from_type; ///< AI_NO_TYPE/AI_CITY/AI_INDUSTRY
- int to_ic;
- byte to_type;
-};
-extern CompanyAiNew _companies_ainew[MAX_COMPANIES];
-
-#endif /* AI_TROLLY_H */