(svn r23354) -Codechange: move all src/ai/api/ai_*.[hc]pp files to src/script/api/script_* (Rubidium)

This commit is contained in:
truebrain
2011-11-29 23:07:38 +00:00
parent 5f6dc24663
commit afdb67a353
161 changed files with 1197 additions and 1195 deletions

View File

@@ -0,0 +1,34 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_accounting.cpp Implementation of AIAccounting. */
#include "../../stdafx.h"
#include "script_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);
}

View File

@@ -0,0 +1,65 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_accounting.hpp Everything to handle AI accounting things. */
#ifndef SCRIPT_ACCOUNTING_HPP
#define SCRIPT_ACCOUNTING_HPP
#include "script_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:
* <pre>
* {
* local costs = AIAccounting();
* BuildRoad(from_here, to_here);
* BuildRoad(from_there, to_there);
* print("Costs for route is: " + costs.GetCosts());
* }
* </pre>
*/
class AIAccounting : public AIObject {
public:
/**
* Creating instance of this class starts counting the costs of commands
* from zero. Saves the current value of GetCosts so we can return to
* the old value when the instance gets deleted.
*/
AIAccounting();
/**
* Restore the AIAccounting that was on top when we created this instance.
* So basically restore the value of GetCosts to what it was before we
* created this instance.
*/
~AIAccounting();
/**
* Get the current value of the costs.
* @return The current costs.
* @note when nesting AIAccounting instances all instances' GetCosts
* will always return the value of the 'top' instance.
*/
Money GetCosts();
/**
* Reset the costs to zero.
* @note when nesting AIAccounting instances all instances' ResetCosts
* will always effect on the 'top' instance.
*/
void ResetCosts();
private:
Money last_costs; ///< The last cost we did return.
};
#endif /* SCRIPT_ACCOUNTING_HPP */

View File

@@ -0,0 +1,153 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_airport.cpp Implementation of AIAirport. */
#include "../../stdafx.h"
#include "script_airport.hpp"
#include "script_station.hpp"
#include "../../station_base.h"
#include "../../company_func.h"
#include "../../town.h"
/* static */ bool AIAirport::IsValidAirportType(AirportType type)
{
return IsAirportInformationAvailable(type) && ::AirportSpec::Get(type)->IsAvailable();
}
/* static */ bool AIAirport::IsAirportInformationAvailable(AirportType type)
{
return type >= 0 && type < (AirportType)NUM_AIRPORTS && AirportSpec::Get(type)->enabled;
}
/* static */ Money AIAirport::GetPrice(AirportType type)
{
if (!IsValidAirportType(type)) return -1;
const AirportSpec *as = ::AirportSpec::Get(type);
return _price[PR_BUILD_STATION_AIRPORT] * as->size_x * as->size_y;
}
/* 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 */ int32 AIAirport::GetAirportWidth(AirportType type)
{
if (!IsAirportInformationAvailable(type)) return -1;
return ::AirportSpec::Get(type)->size_x;
}
/* static */ int32 AIAirport::GetAirportHeight(AirportType type)
{
if (!IsAirportInformationAvailable(type)) return -1;
return ::AirportSpec::Get(type)->size_y;
}
/* static */ int32 AIAirport::GetAirportCoverageRadius(AirportType type)
{
if (!IsAirportInformationAvailable(type)) return -1;
return _settings_game.station.modified_catchment ? ::AirportSpec::Get(type)->catchment : (uint)CA_UNMODIFIED;
}
/* static */ bool AIAirport::BuildAirport(TileIndex tile, AirportType type, StationID station_id)
{
EnforcePrecondition(false, ::IsValidTile(tile));
EnforcePrecondition(false, IsValidAirportType(type));
EnforcePrecondition(false, station_id == AIStation::STATION_NEW || station_id == AIStation::STATION_JOIN_ADJACENT || AIStation::IsValidStation(station_id));
uint p2 = station_id == AIStation::STATION_JOIN_ADJACENT ? 0 : 1;
p2 |= (AIStation::IsValidStation(station_id) ? station_id : INVALID_STATION) << 16;
return AIObject::DoCommand(tile, type, p2, 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 = ::Station::GetByTile(tile);
if (st->owner != _current_company) return -1;
if ((st->facilities & FACIL_AIRPORT) == 0) return -1;
return st->airport.GetNumHangars();
}
/* 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 = ::Station::GetByTile(tile);
if (st->owner != _current_company) return INVALID_TILE;
if ((st->facilities & FACIL_AIRPORT) == 0) return INVALID_TILE;
return st->airport.GetHangarTile(0);
}
/* 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)::Station::Get(station_id)->airport.type;
}
/* static */ int AIAirport::GetNoiseLevelIncrease(TileIndex tile, AirportType type)
{
extern Town *AirportGetNearestTown(const AirportSpec *as, TileIndex airport_tile);
extern uint8 GetAirportNoiseLevelForTown(const AirportSpec *as, TileIndex town_tile, TileIndex tile);
if (!::IsValidTile(tile)) return -1;
if (!IsAirportInformationAvailable(type)) return -1;
if (_settings_game.economy.station_noise_level) {
const AirportSpec *as = ::AirportSpec::Get(type);
const Town *t = AirportGetNearestTown(as, tile);
return GetAirportNoiseLevelForTown(as, t->xy, tile);
}
return 1;
}
/* static */ TownID AIAirport::GetNearestTown(TileIndex tile, AirportType type)
{
extern Town *AirportGetNearestTown(const AirportSpec *as, TileIndex airport_tile);
if (!::IsValidTile(tile)) return INVALID_TOWN;
if (!IsAirportInformationAvailable(type)) return INVALID_TOWN;
return AirportGetNearestTown(AirportSpec::Get(type), tile)->index;
}

View File

@@ -0,0 +1,197 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_airport.hpp Everything to query and build airports. */
#ifndef SCRIPT_AIRPORT_HPP
#define SCRIPT_AIRPORT_HPP
#include "script_object.hpp"
/**
* Class that handles all airport related functions.
*/
class AIAirport : public AIObject {
public:
/**
* 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 and available.
* @param type The AirportType to check.
* @return True if and only if the AirportType is valid and available.
* @post return value == true -> IsAirportInformationAvailable returns true.
*/
static bool IsValidAirportType(AirportType type);
/**
* Can you get information on this airport type? As opposed to
* IsValidAirportType this will return also return true when
* an airport type is no longer buildable.
* @param type The AirportType to check.
* @return True if and only if the AirportType is valid.
* @post return value == false -> IsValidAirportType returns false.
*/
static bool IsAirportInformationAvailable(AirportType type);
/**
* Get the cost to build this AirportType.
* @param type The AirportType to check.
* @pre AirportAvailable(type).
* @return The cost of building this AirportType.
*/
static Money GetPrice(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 an airport.
* @param tile The tile to check.
* @pre AIMap::IsValidTile(tile).
* @return True if and only if the tile has an airport.
*/
static bool IsAirportTile(TileIndex tile);
/**
* Get the width of this type of airport.
* @param type The type of airport.
* @pre IsAirportInformationAvailable(type).
* @return The width in tiles.
*/
static int32 GetAirportWidth(AirportType type);
/**
* Get the height of this type of airport.
* @param type The type of airport.
* @pre IsAirportInformationAvailable(type).
* @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.
* @pre IsAirportInformationAvailable(type).
* @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 station_id The station to join, AIStation::STATION_NEW or AIStation::STATION_JOIN_ADJACENT.
* @pre AIMap::IsValidTile(tile).
* @pre AirportAvailable(type).
* @pre station_id == AIStation::STATION_NEW || station_id == AIStation::STATION_JOIN_ADJACENT || AIStation::IsValidStation(station_id).
* @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_ANOTHER_STATION
* @return Whether the airport has been/can be build or not.
*/
static bool BuildAirport(TileIndex tile, AirportType type, StationID station_id);
/**
* Removes an 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.
* @pre IsAirportInformationAvailable(type).
* @return The amount of noise added to the nearest town.
* @note The noise will be added to the town with TownID GetNearestTown(tile, type).
*/
static int GetNoiseLevelIncrease(TileIndex tile, AirportType type);
/**
* Get the TownID of the town whose local authority will influence
* an airport at some tile.
* @param tile The tile to check.
* @param type The AirportType to check.
* @pre IsAirportInformationAvailable(type).
* @return The TownID of the town closest to the tile.
*/
static TownID GetNearestTown(TileIndex tile, AirportType type);
};
#endif /* SCRIPT_AIRPORT_HPP */

View File

@@ -0,0 +1,51 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_base.cpp Implementation of AIBase. */
#include "../../stdafx.h"
#include "script_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);
}

View File

@@ -0,0 +1,76 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_base.hpp Everything to query basic things. */
#ifndef SCRIPT_BASE_HPP
#define SCRIPT_BASE_HPP
#include "script_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:
/**
* Get a random value.
* @return A random value between 0 and MAX(uint32).
*/
static uint32 Rand();
/**
* Get a random value.
* @param unused_param This parameter 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 parameter 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 approximately '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 approximately 'out' times true when called 'max' times.
* After all, it is a random function.
* @param unused_param This parameter 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 /* SCRIPT_BASE_HPP */

View File

@@ -0,0 +1,59 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_basestation.cpp Implementation of AIBaseStation. */
#include "../../stdafx.h"
#include "script_basestation.hpp"
#include "../../station_base.h"
#include "../../string_func.h"
#include "../../strings_func.h"
#include "../../company_func.h"
#include "table/strings.h"
/* static */ bool AIBaseStation::IsValidBaseStation(StationID station_id)
{
const BaseStation *st = ::BaseStation::GetIfValid(station_id);
return st != NULL && (st->owner == _current_company || st->owner == OWNER_NONE);
}
/* static */ char *AIBaseStation::GetName(StationID station_id)
{
if (!IsValidBaseStation(station_id)) return NULL;
static const int len = 64;
char *name = MallocT<char>(len);
::SetDParam(0, station_id);
::GetString(name, ::Station::IsValidID(station_id) ? STR_STATION_NAME : STR_WAYPOINT_NAME, &name[len - 1]);
return name;
}
/* static */ bool AIBaseStation::SetName(StationID station_id, const char *name)
{
EnforcePrecondition(false, IsValidBaseStation(station_id));
EnforcePrecondition(false, !::StrEmpty(name));
EnforcePreconditionCustomError(false, ::Utf8StringLength(name) < MAX_LENGTH_STATION_NAME_CHARS, AIError::ERR_PRECONDITION_STRING_TOO_LONG);
return AIObject::DoCommand(0, station_id, 0, ::Station::IsValidID(station_id) ? CMD_RENAME_STATION : CMD_RENAME_WAYPOINT, name);
}
/* static */ TileIndex AIBaseStation::GetLocation(StationID station_id)
{
if (!IsValidBaseStation(station_id)) return INVALID_TILE;
return ::BaseStation::Get(station_id)->xy;
}
/* static */ int32 AIBaseStation::GetConstructionDate(StationID station_id)
{
if (!IsValidBaseStation(station_id)) return -1;
return ::BaseStation::Get(station_id)->build_date;
}

View File

@@ -0,0 +1,78 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_basestation.hpp Base for stations/waypoint handling. */
#ifndef SCRIPT_BASESTATION_HPP
#define SCRIPT_BASESTATION_HPP
#include "script_error.hpp"
/**
* Base class for stations and waypoints.
*/
class AIBaseStation : public AIObject {
public:
/**
* Special station IDs for building adjacent/new stations when
* the adjacent/distant join features are enabled.
*/
enum SpecialStationIDs {
STATION_NEW = 0xFFFD, ///< Build a new station
STATION_JOIN_ADJACENT = 0xFFFE, ///< Join an neighbouring station if one exists
STATION_INVALID = 0xFFFF, ///< Invalid station id.
};
/**
* Checks whether the given basestation is valid and owned by you.
* @param station_id The station to check.
* @return True if and only if the basestation is valid.
* @note IsValidBaseStation == (IsValidStation || IsValidWaypoint).
*/
static bool IsValidBaseStation(StationID station_id);
/**
* Get the name of a basestation.
* @param station_id The basestation to get the name of.
* @pre IsValidBaseStation(station_id).
* @return The name of the station.
*/
static char *GetName(StationID station_id);
/**
* Set the name this basestation.
* @param station_id The basestation to set the name of.
* @param name The new name of the station.
* @pre IsValidBaseStation(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 basestation.
* @param station_id The basestation to get the location of.
* @pre IsValidBaseStation(station_id).
* @return The tile the basestation sign above it.
* @note The tile is not necessarily a station tile (and if it is, it could also belong to another station).
* @see AITileList_StationType.
*/
static TileIndex GetLocation(StationID station_id);
/**
* Get the last date a station part was added to this station.
* @param station_id The station to look at.
* @return The last date some part of this station was build.
*/
static int32 GetConstructionDate(StationID station_id);
};
#endif /* SCRIPT_BASESTATION_HPP */

View File

@@ -0,0 +1,179 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_bridge.cpp Implementation of AIBridge. */
#include "../../stdafx.h"
#include "script_bridge.hpp"
#include "script_rail.hpp"
#include "../../ai/ai_instance.hpp"
#include "../../bridge_map.h"
#include "../../strings_func.h"
#include "../../economy_func.h"
#include "../../date_func.h"
/* static */ bool AIBridge::IsValidBridge(BridgeID bridge_id)
{
return bridge_id < MAX_BRIDGES && ::GetBridgeSpec(bridge_id)->avail_year <= _cur_year;
}
/* static */ bool AIBridge::IsBridgeTile(TileIndex tile)
{
if (!::IsValidTile(tile)) return false;
return ::IsBridgeTile(tile);
}
/* static */ BridgeID AIBridge::GetBridgeID(TileIndex tile)
{
if (!IsBridgeTile(tile)) return (BridgeID)-1;
return (BridgeID)::GetBridgeType(tile);
}
/**
* Helper function to connect a just built bridge to nearby roads.
* @param instance The AI we have to built the road for.
*/
static void _DoCommandReturnBuildBridge2(class AIInstance *instance)
{
if (!AIBridge::_BuildBridgeRoad2()) {
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();
}
/**
* Helper function to connect a just built bridge to nearby roads.
* @param instance The AI we have to built the road for.
*/
static void _DoCommandReturnBuildBridge1(class AIInstance *instance)
{
if (!AIBridge::_BuildBridgeRoad1()) {
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::VT_ROAD || vehicle_type == AIVehicle::VT_RAIL || vehicle_type == AIVehicle::VT_WATER);
EnforcePrecondition(false, vehicle_type != AIVehicle::VT_RAIL || AIRail::IsRailTypeAvailable(AIRail::GetCurrentRailType()));
uint type = 0;
switch (vehicle_type) {
case AIVehicle::VT_ROAD:
type |= (TRANSPORT_ROAD << 15);
type |= (::RoadTypeToRoadTypes((::RoadType)AIObject::GetRoadType()) << 8);
break;
case AIVehicle::VT_RAIL:
type |= (TRANSPORT_RAIL << 15);
type |= (AIRail::GetCurrentRailType() << 8);
break;
case AIVehicle::VT_WATER:
type |= (TRANSPORT_WATER << 15);
break;
default: NOT_REACHED();
}
/* For rail and water we do nothing special */
if (vehicle_type == AIVehicle::VT_RAIL || vehicle_type == AIVehicle::VT_WATER) {
return AIObject::DoCommand(end, start, type | bridge_id, CMD_BUILD_BRIDGE);
}
AIObject::SetCallbackVariable(0, start);
AIObject::SetCallbackVariable(1, end);
return AIObject::DoCommand(end, start, type | bridge_id, CMD_BUILD_BRIDGE, NULL, &::_DoCommandReturnBuildBridge1);
}
/* static */ bool AIBridge::_BuildBridgeRoad1()
{
/* Build the piece of road on the 'start' side of the bridge */
TileIndex end = AIObject::GetCallbackVariable(0);
TileIndex start = AIObject::GetCallbackVariable(1);
DiagDirection dir_1 = ::DiagdirBetweenTiles(end, start);
DiagDirection dir_2 = ::ReverseDiagDir(dir_1);
return AIObject::DoCommand(start + ::TileOffsByDiagDir(dir_1), ::DiagDirToRoadBits(dir_2) | (AIObject::GetRoadType() << 4), 0, CMD_BUILD_ROAD, NULL, &::_DoCommandReturnBuildBridge2);
}
/* static */ bool AIBridge::_BuildBridgeRoad2()
{
/* Build the piece of road on the 'end' side of the bridge */
TileIndex end = AIObject::GetCallbackVariable(0);
TileIndex start = AIObject::GetCallbackVariable(1);
DiagDirection dir_1 = ::DiagdirBetweenTiles(end, start);
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 */ 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; // km-ish/h
}
/* static */ Money AIBridge::GetPrice(BridgeID bridge_id, uint length)
{
if (!IsValidBridge(bridge_id)) return -1;
return ::CalcBridgeLenCostFactor(length) * _price[PR_BUILD_BRIDGE] * ::GetBridgeSpec(bridge_id)->price >> 8;
}
/* static */ int32 AIBridge::GetMaxLength(BridgeID bridge_id)
{
if (!IsValidBridge(bridge_id)) return -1;
return min(::GetBridgeSpec(bridge_id)->max_length, _settings_game.construction.max_bridge_length) + 2;
}
/* static */ int32 AIBridge::GetMinLength(BridgeID bridge_id)
{
if (!IsValidBridge(bridge_id)) return -1;
return ::GetBridgeSpec(bridge_id)->min_length + 2;
}
/* static */ TileIndex AIBridge::GetOtherBridgeEnd(TileIndex tile)
{
if (!::IsValidTile(tile)) return INVALID_TILE;
if (!IsBridgeTile(tile)) return INVALID_TILE;
return ::GetOtherBridgeEnd(tile);
}

View File

@@ -0,0 +1,169 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_bridge.hpp Everything to query and build bridges. */
#ifndef SCRIPT_BRIDGE_HPP
#define SCRIPT_BRIDGE_HPP
#include "script_vehicle.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_ERROR_CAN_T_BUILD_BRIDGE_HERE]
/** One (or more) of the bridge head(s) ends in water. */
ERR_BRIDGE_CANNOT_END_IN_WATER, // [STR_ERROR_ENDS_OF_BRIDGE_MUST_BOTH]
/** The bride heads need to be on the same height */
ERR_BRIDGE_HEADS_NOT_ON_SAME_HEIGHT, // [STR_ERROR_BRIDGEHEADS_NOT_SAME_HEIGHT]
};
/**
* 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 BridgeID of a bridge at a given tile.
* @param tile The tile to get the BridgeID from.
* @pre IsBridgeTile(tile).
* @return The BridgeID from the bridge at tile 'tile'.
*/
static BridgeID GetBridgeID(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 char *GetName(BridgeID bridge_id);
/**
* Get the maximum speed of a bridge.
* @param bridge_id The bridge to get the maximum speed of.
* @pre IsValidBridge(bridge_id).
* @return The maximum speed the bridge has.
* @note The speed is in OpenTTD's internal speed unit.
* This is mph / 1.6, which is roughly km/h.
* To get km/h multiply this number by 1.00584.
*/
static int32 GetMaxSpeed(BridgeID bridge_id);
/**
* Get the new cost of a bridge, excluding the road and/or rail.
* @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);
#ifndef DOXYGEN_AI_DOCS
/**
* 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::VT_ROAD || vehicle_type == AIVehicle::VT_WATER ||
* (vehicle_type == AIVehicle::VT_RAIL && AIRail::IsRailTypeAvailable(AIRail::GetCurrentRailType())).
* @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 /* SCRIPT_BRIDGE_HPP */

View File

@@ -0,0 +1,31 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_bridgelist.cpp Implementation of AIBridgeList and friends. */
#include "../../stdafx.h"
#include "script_bridgelist.hpp"
#include "script_bridge.hpp"
#include "../../bridge.h"
AIBridgeList::AIBridgeList()
{
for (byte j = 0; j < MAX_BRIDGES; j++) {
if (AIBridge::IsValidBridge(j)) this->AddItem(j);
}
}
AIBridgeList_Length::AIBridgeList_Length(uint length)
{
for (byte j = 0; j < MAX_BRIDGES; j++) {
if (AIBridge::IsValidBridge(j)) {
if (length >= (uint)AIBridge::GetMinLength(j) && length <= (uint)AIBridge::GetMaxLength(j)) this->AddItem(j);
}
}
}

View File

@@ -0,0 +1,38 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_bridgelist.hpp List all the bridges. */
#ifndef SCRIPT_BRIDGELIST_HPP
#define SCRIPT_BRIDGELIST_HPP
#include "script_list.hpp"
/**
* Create a list of bridges.
* @ingroup AIList
*/
class AIBridgeList : public AIList {
public:
AIBridgeList();
};
/**
* Create a list of bridges that can be built on a specific length.
* @ingroup AIList
*/
class AIBridgeList_Length : public AIList {
public:
/**
* @param length The length of the bridge you want to build.
*/
AIBridgeList_Length(uint length);
};
#endif /* SCRIPT_BRIDGELIST_HPP */

View File

@@ -0,0 +1,67 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_cargo.cpp Implementation of AICargo. */
#include "../../stdafx.h"
#include "script_cargo.hpp"
#include "../../cargotype.h"
#include "../../economy_func.h"
#include "../../core/bitmath_func.hpp"
/* static */ bool AICargo::IsValidCargo(CargoID cargo_type)
{
return (cargo_type < NUM_CARGO && ::CargoSpec::Get(cargo_type)->IsValid());
}
/* static */ bool AICargo::IsValidTownEffect(TownEffect towneffect_type)
{
return (towneffect_type >= (TownEffect)TE_BEGIN && towneffect_type < (TownEffect)TE_END);
}
/* static */ char *AICargo::GetCargoLabel(CargoID cargo_type)
{
if (!IsValidCargo(cargo_type)) return NULL;
const CargoSpec *cargo = ::CargoSpec::Get(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 = ::CargoSpec::Get(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)::CargoSpec::Get(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);
}

View File

@@ -0,0 +1,121 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_cargo.hpp Everything to query cargos. */
#ifndef SCRIPT_CARGO_HPP
#define SCRIPT_CARGO_HPP
#include "script_object.hpp"
/**
* Class that handles all cargo related functions.
*/
class AICargo : public AIObject {
public:
/**
* The classes of cargo (from newgrf_cargo.h).
*/
enum CargoClass {
CC_PASSENGERS = 1 << 0, ///< Passengers. Cargos of this class appear at bus stops. Cargos not of this class appear at truck stops.
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
};
/**
* Special cargo types.
*/
enum SpecialCargoID {
CT_AUTO_REFIT = 0xFD, ///< Automatically choose cargo type when doing auto-refitting.
CT_NO_REFIT = 0xFE, ///< Do not refit cargo of a vehicle.
};
/**
* 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);
/**
* Checks whether the given town effect type is valid.
* @param towneffect_type The town effect to check.
* @return True if and only if the town effect type is valid.
*/
static bool IsValidTownEffect(TownEffect towneffect_type);
/**
* Gets the string representation of the cargo label.
* @param cargo_type The cargo to get the string representation of.
* @pre AICargo::IsValidCargo(cargo_type).
* @return The cargo label.
* @note Never use this to check if it is a certain cargo. NewGRF can
* redefine all of the names.
*/
static char *GetCargoLabel(CargoID cargo_type);
/**
* Checks whether the give cargo is a freight or not.
* This defines whether the "freight train weight multiplier" will apply to
* trains transporting this cargo.
* @param cargo_type The cargo to check on.
* @pre AICargo::IsValidCargo(cargo_type).
* @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.
* @pre AICargo::IsValidCargo(cargo_type).
* @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.
* @pre AICargo::IsValidCargo(cargo_type).
* @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.
* @pre AICargo::IsValidCargo(cargo_type).
* @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 /* SCRIPT_CARGO_HPP */

View File

@@ -0,0 +1,62 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_cargolist.cpp Implementation of AICargoList and friends. */
#include "../../stdafx.h"
#include "script_cargolist.hpp"
#include "script_industry.hpp"
#include "script_station.hpp"
#include "../../cargotype.h"
#include "../../industry.h"
#include "../../station_base.h"
AICargoList::AICargoList()
{
const CargoSpec *cs;
FOR_ALL_CARGOSPECS(cs) {
this->AddItem(cs->Index());
}
}
AICargoList_IndustryAccepting::AICargoList_IndustryAccepting(IndustryID industry_id)
{
if (!AIIndustry::IsValidIndustry(industry_id)) return;
Industry *ind = ::Industry::Get(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 = ::Industry::Get(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);
}
}
}
AICargoList_StationAccepting::AICargoList_StationAccepting(StationID station_id)
{
if (!AIStation::IsValidStation(station_id)) return;
Station *st = ::Station::Get(station_id);
for (CargoID i = 0; i < NUM_CARGO; i++) {
if (HasBit(st->goods[i].acceptance_pickup, GoodsEntry::GES_ACCEPTANCE)) this->AddItem(i);
}
}

View File

@@ -0,0 +1,64 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_cargolist.hpp List all the cargos. */
#ifndef SCRIPT_CARGOLIST_HPP
#define SCRIPT_CARGOLIST_HPP
#include "script_list.hpp"
/**
* Creates a list of cargos that can be produced in the current game.
* @ingroup AIList
*/
class AICargoList : public AIList {
public:
AICargoList();
};
/**
* Creates a list of cargos that the given industry accepts.
* @note This list also includes cargos that are temporarily not accepted
* by this industry, @see AIIndustry::IsCargoAccepted.
* @ingroup AIList
*/
class AICargoList_IndustryAccepting : public AIList {
public:
/**
* @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 AIList {
public:
/**
* @param industry_id The industry to get the list of cargos it produces from.
*/
AICargoList_IndustryProducing(IndustryID industry_id);
};
/**
* Creates a list of cargos that the given station accepts.
* @ingroup AIList
*/
class AICargoList_StationAccepting : public AIList {
public:
/**
* @param station_id The station to get the list of cargos it accepts from.
*/
AICargoList_StationAccepting(StationID station_id);
};
#endif /* SCRIPT_CARGOLIST_HPP */

View File

@@ -0,0 +1,267 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_company.cpp Implementation of AICompany. */
#include "../../stdafx.h"
#include "script_company.hpp"
#include "script_error.hpp"
#include "../../command_func.h"
#include "../../company_func.h"
#include "../../company_base.h"
#include "../../company_manager_face.h"
#include "../../economy_func.h"
#include "../../object_type.h"
#include "../../strings_func.h"
#include "../../tile_map.h"
#include "../../string_func.h"
#include "../../settings_func.h"
#include "table/strings.h"
/* static */ AICompany::CompanyID AICompany::ResolveCompanyID(AICompany::CompanyID company)
{
if (company == COMPANY_SELF) return (CompanyID)((byte)_current_company);
return ::Company::IsValidID((::CompanyID)company) ? company : COMPANY_INVALID;
}
/* static */ bool AICompany::IsMine(AICompany::CompanyID company)
{
return ResolveCompanyID(company) == ResolveCompanyID(COMPANY_SELF);
}
/* static */ bool AICompany::SetName(const char *name)
{
EnforcePrecondition(false, !::StrEmpty(name));
EnforcePreconditionCustomError(false, ::Utf8StringLength(name) < MAX_LENGTH_COMPANY_NAME_CHARS, AIError::ERR_PRECONDITION_STRING_TOO_LONG);
return AIObject::DoCommand(0, 0, 0, CMD_RENAME_COMPANY, name);
}
/* static */ char *AICompany::GetName(AICompany::CompanyID company)
{
company = ResolveCompanyID(company);
if (company == COMPANY_INVALID) 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 */ char *AICompany::GetPresidentName(AICompany::CompanyID company)
{
company = ResolveCompanyID(company);
static const int len = 64;
char *president_name = MallocT<char>(len);
if (company != COMPANY_INVALID) {
::SetDParam(0, company);
::GetString(president_name, STR_PRESIDENT_NAME, &president_name[len - 1]);
} else {
*president_name = '\0';
}
return president_name;
}
/* static */ bool AICompany::SetPresidentGender(Gender gender)
{
EnforcePrecondition(false, gender == GENDER_MALE || gender == GENDER_FEMALE);
EnforcePrecondition(false, GetPresidentGender(AICompany::COMPANY_SELF) != gender);
CompanyManagerFace cmf;
GenderEthnicity ge = (GenderEthnicity)((gender == GENDER_FEMALE ? (1 << ::GENDER_FEMALE) : 0) | (::InteractiveRandom() & (1 << ETHNICITY_BLACK)));
RandomCompanyManagerFaceBits(cmf, ge, false);
return AIObject::DoCommand(0, 0, cmf, CMD_SET_COMPANY_MANAGER_FACE);
}
/* static */ AICompany::Gender AICompany::GetPresidentGender(CompanyID company)
{
company = ResolveCompanyID(company);
if (company == COMPANY_INVALID) return GENDER_INVALID;
GenderEthnicity ge = (GenderEthnicity)GetCompanyManagerFaceBits(Company::Get(company)->face, CMFV_GEN_ETHN, GE_WM);
return HasBit(ge, ::GENDER_FEMALE) ? GENDER_FEMALE : GENDER_MALE;
}
/* static */ Money AICompany::GetQuarterlyIncome(AICompany::CompanyID company, uint32 quarter)
{
company = ResolveCompanyID(company);
if (company == COMPANY_INVALID) return -1;
if (quarter > EARLIEST_QUARTER) return -1;
if (quarter == CURRENT_QUARTER) {
return ::Company::Get((::CompanyID)company)->cur_economy.income;
}
return ::Company::Get((::CompanyID)company)->old_economy[quarter - 1].income;
}
/* static */ Money AICompany::GetQuarterlyExpenses(AICompany::CompanyID company, uint32 quarter)
{
company = ResolveCompanyID(company);
if (company == COMPANY_INVALID) return -1;
if (quarter > EARLIEST_QUARTER) return -1;
if (quarter == CURRENT_QUARTER) {
return ::Company::Get((::CompanyID)company)->cur_economy.expenses;
}
return ::Company::Get((::CompanyID)company)->old_economy[quarter - 1].expenses;
}
/* static */ int32 AICompany::GetQuarterlyCargoDelivered(AICompany::CompanyID company, uint32 quarter)
{
company = ResolveCompanyID(company);
if (company == COMPANY_INVALID) return -1;
if (quarter > EARLIEST_QUARTER) return -1;
if (quarter == CURRENT_QUARTER) {
return ::Company::Get((::CompanyID)company)->cur_economy.delivered_cargo;
}
return ::Company::Get((::CompanyID)company)->old_economy[quarter - 1].delivered_cargo;
}
/* static */ int32 AICompany::GetQuarterlyPerformanceRating(AICompany::CompanyID company, uint32 quarter)
{
company = ResolveCompanyID(company);
if (company == COMPANY_INVALID) return -1;
if (quarter > EARLIEST_QUARTER) return -1;
if (quarter == CURRENT_QUARTER) return -1;
return ::Company::Get((::CompanyID)company)->old_economy[quarter - 1].performance_history;
}
/* static */ Money AICompany::GetQuarterlyCompanyValue(AICompany::CompanyID company, uint32 quarter)
{
company = ResolveCompanyID(company);
if (company == COMPANY_INVALID) return -1;
if (quarter > EARLIEST_QUARTER) return -1;
if (quarter == CURRENT_QUARTER) {
return ::CalculateCompanyValue(::Company::Get((::CompanyID)company));
}
return ::Company::Get((::CompanyID)company)->old_economy[quarter - 1].company_value;
}
/* static */ Money AICompany::GetBankBalance(AICompany::CompanyID company)
{
company = ResolveCompanyID(company);
if (company == COMPANY_INVALID) return -1;
return ::Company::Get((CompanyID)company)->money;
}
/* static */ Money AICompany::GetLoanAmount()
{
return ::Company::Get(_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(COMPANY_SELF)) >= 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, OBJECT_HQ, 0, CMD_BUILD_OBJECT);
}
/* static */ TileIndex AICompany::GetCompanyHQ(CompanyID company)
{
company = ResolveCompanyID(company);
if (company == COMPANY_INVALID) return INVALID_TILE;
TileIndex loc = ::Company::Get((CompanyID)company)->location_of_HQ;
return (loc == 0) ? INVALID_TILE : loc;
}
/* static */ bool AICompany::SetAutoRenewStatus(bool autorenew)
{
return AIObject::DoCommand(0, ::GetCompanySettingIndex("company.engine_renew"), autorenew ? 1 : 0, CMD_CHANGE_COMPANY_SETTING);
}
/* static */ bool AICompany::GetAutoRenewStatus(CompanyID company)
{
company = ResolveCompanyID(company);
if (company == COMPANY_INVALID) return false;
return ::Company::Get((CompanyID)company)->settings.engine_renew;
}
/* static */ bool AICompany::SetAutoRenewMonths(int16 months)
{
return AIObject::DoCommand(0, ::GetCompanySettingIndex("company.engine_renew_months"), months, CMD_CHANGE_COMPANY_SETTING);
}
/* static */ int16 AICompany::GetAutoRenewMonths(CompanyID company)
{
company = ResolveCompanyID(company);
if (company == COMPANY_INVALID) return 0;
return ::Company::Get((CompanyID)company)->settings.engine_renew_months;
}
/* static */ bool AICompany::SetAutoRenewMoney(uint32 money)
{
return AIObject::DoCommand(0, ::GetCompanySettingIndex("company.engine_renew_money"), money, CMD_CHANGE_COMPANY_SETTING);
}
/* static */ uint32 AICompany::GetAutoRenewMoney(CompanyID company)
{
company = ResolveCompanyID(company);
if (company == COMPANY_INVALID) return 0;
return ::Company::Get((CompanyID)company)->settings.engine_renew_money;
}

View File

@@ -0,0 +1,281 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_company.hpp Everything to query a company's financials and statistics or build company related buildings. */
#ifndef SCRIPT_COMPANY_HPP
#define SCRIPT_COMPANY_HPP
#include "script_object.hpp"
/**
* Class that handles all company related functions.
*/
class AICompany : public AIObject {
public:
/** The range of possible quarters to get company information of. */
enum Quarter {
CURRENT_QUARTER = 0, ///< The current quarter.
EARLIEST_QUARTER = MAX_HISTORY_QUARTERS, ///< The earliest quarter company information is available for.
};
/** Different constants related to CompanyID. */
enum CompanyID {
COMPANY_FIRST = 0, ///< The first available company.
COMPANY_LAST = ::MAX_COMPANIES, ///< The last available company.
COMPANY_SELF = 254, ///< Constant that gets resolved to the correct company index for your company.
COMPANY_INVALID = -1, ///< An invalid company.
};
/** Possible genders for company presidents. */
enum Gender {
GENDER_MALE, ///< A male person.
GENDER_FEMALE, ///< A female person.
GENDER_INVALID = -1, ///< An invalid gender.
};
/**
* Resolved the given company index to the correct index for the company. If
* the company index was COMPANY_SELF it will be resolved to the index of
* your company. If the company with the given index does not exist it will
* return COMPANY_INVALID.
* @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);
/**
* 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);
/**
* Get the name of the given company.
* @param company The company to get the name for.
* @pre ResolveCompanyID(company) != COMPANY_INVALID.
* @return The name of the given company.
*/
static 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) != COMPANY_INVALID.
* @return The name of the president of the given company.
*/
static char *GetPresidentName(CompanyID company);
/**
* Set the gender of the president of your company.
* @param gender The new gender for your president.
* @pre GetPresidentGender(AICompany.COMPANY_SELF) != gender.
* @return True if the gender was changed.
* @note When successful a random face will be created.
*/
static bool SetPresidentGender(Gender gender);
/**
* Get the gender of the president of the given company.
* @param company The company to get the presidents gender off.
* @return The gender of the president.
*/
static Gender GetPresidentGender(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 'loan'.
* @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 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 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 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) != COMPANY_INVALID.
* @return The actual bank balance.
*/
static Money GetBankBalance(CompanyID company);
/**
* Get the income of the company in the given quarter.
* @param company The company to get the quarterly income of.
* @param quarter The quarter to get the income of.
* @pre ResolveCompanyID(company) != COMPANY_INVALID.
* @pre quarter <= EARLIEST_QUARTER.
* @return The bruto income of the company in the given quarter.
*/
static Money GetQuarterlyIncome(CompanyID company, uint32 quarter);
/**
* Get the expenses of the company in the given quarter.
* @param company The company to get the quarterly expenses of.
* @param quarter The quarter to get the expenses of.
* @pre ResolveCompanyID(company) != COMPANY_INVALID.
* @pre quarter <= EARLIEST_QUARTER.
* @return The expenses of the company in the given quarter.
*/
static Money GetQuarterlyExpenses(CompanyID company, uint32 quarter);
/**
* Get the amount of cargo delivered by the given company in the given quarter.
* @param company The company to get the amount of delivered cargo of.
* @param quarter The quarter to get the amount of delivered cargo of.
* @pre ResolveCompanyID(company) != COMPANY_INVALID.
* @pre quarter <= EARLIEST_QUARTER.
* @return The amount of cargo delivered by the given company in the given quarter.
*/
static int32 GetQuarterlyCargoDelivered(CompanyID company, uint32 quarter);
/**
* Get the performance rating of the given company in the given quarter.
* @param company The company to get the performance rating of.
* @param quarter The quarter to get the performance rating of.
* @pre ResolveCompanyID(company) != COMPANY_INVALID.
* @pre quarter <= EARLIEST_QUARTER.
* @pre quarter != CURRENT_QUARTER.
* @note The performance rating is calculated after every quarter, so the value for CURRENT_QUARTER is undefined.
* @return The performance rating of the given company in the given quarter.
*/
static int32 GetQuarterlyPerformanceRating(CompanyID company, uint32 quarter);
/**
* Get the value of the company in the given quarter.
* @param company The company to get the value of.
* @param quarter The quarter to get the value of.
* @pre ResolveCompanyID(company) != COMPANY_INVALID.
* @pre quarter <= EARLIEST_QUARTER.
* @return The value of the company in the given quarter.
*/
static Money GetQuarterlyCompanyValue(CompanyID company, uint32 quarter);
/**
* 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) != COMPANY_INVALID.
* @return The tile of the company's HQ, this tile is the most nothern tile
* of that HQ, or AIMap::TILE_INVALID 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) != COMPANY_INVALID.
* @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) != COMPANY_INVALID.
* @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) != COMPANY_INVALID.
* @return The minimum required money for autorenew to work.
*/
static uint32 GetAutoRenewMoney(CompanyID company);
};
DECLARE_POSTFIX_INCREMENT(AICompany::CompanyID)
#endif /* SCRIPT_COMPANY_HPP */

View File

@@ -0,0 +1,163 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_controller.cpp Implementation of AIControler. */
#include "../../stdafx.h"
#include "../../string_func.h"
#include "../../company_base.h"
#include "../../company_func.h"
#include "../../script/squirrel.hpp"
#include "../../rev.h"
#include "script_controller.hpp"
#include "../../ai/ai_instance.hpp"
#include "../../ai/ai_config.hpp"
#include "../../ai/ai.hpp"
#include "script_log.hpp"
/* static */ void AIController::SetCommandDelay(int ticks)
{
if (ticks <= 0) return;
AIObject::SetDoCommandDelay(ticks);
}
/* static */ void AIController::Sleep(int ticks)
{
if (!AIObject::CanSuspend()) {
throw AI_FatalError("You are not allowed to call Sleep in your constructor, Save(), Load(), and any valuator.");
}
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((*iter).second);
free((*iter).first);
}
this->loaded_library.clear();
}
/* static */ uint AIController::GetTick()
{
return AIObject::GetActiveInstance()->GetController()->ticks;
}
/* static */ int AIController::GetOpsTillSuspend()
{
return AIObject::GetActiveInstance()->GetOpsTillSuspend();
}
/* static */ int AIController::GetSetting(const char *name)
{
return AIConfig::GetConfig(_current_company)->GetSetting(name);
}
/* static */ uint AIController::GetVersion()
{
return _openttd_newgrf_version;
}
/* static */ HSQOBJECT AIController::Import(const char *library, const char *class_name, int version)
{
AIController *controller = AIObject::GetActiveInstance()->GetController();
Squirrel *engine = AIObject::GetActiveInstance()->engine;
HSQUIRRELVM vm = engine->GetVM();
/* Internally we store libraries as 'library.version' */
char library_name[1024];
snprintf(library_name, sizeof(library_name), "%s.%d", library, version);
strtolower(library_name);
AILibrary *lib = AI::FindLibrary(library, version);
if (lib == NULL) {
char error[1024];
snprintf(error, sizeof(error), "couldn't find library '%s' with version %d", library, version);
throw sq_throwerror(vm, OTTD2SQ(error));
}
/* Get the current table/class we belong to */
HSQOBJECT parent;
sq_getstackobj(vm, 1, &parent);
char fake_class[1024];
LoadedLibraryList::iterator iter = controller->loaded_library.find(library_name);
if (iter != controller->loaded_library.end()) {
ttd_strlcpy(fake_class, (*iter).second, sizeof(fake_class));
} else {
int next_number = ++controller->loaded_library_count;
/* 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, OTTD2SQ(fake_class), -1);
sq_newclass(vm, SQFalse);
/* Load the library */
if (!engine->LoadScript(vm, lib->GetMainScript(), false)) {
char error[1024];
snprintf(error, sizeof(error), "there was a compile error when importing '%s' version %d", library, version);
throw sq_throwerror(vm, OTTD2SQ(error));
}
/* Create the fake class */
sq_newslot(vm, -3, SQFalse);
sq_pop(vm, 1);
controller->loaded_library[strdup(library_name)] = strdup(fake_class);
}
/* Find the real class inside the fake class (like 'sets.Vector') */
sq_pushroottable(vm);
sq_pushstring(vm, OTTD2SQ(fake_class), -1);
if (SQ_FAILED(sq_get(vm, -2))) {
throw sq_throwerror(vm, _SC("internal error assigning library class"));
}
sq_pushstring(vm, OTTD2SQ(lib->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", lib->GetInstanceName(), library, version);
throw sq_throwerror(vm, OTTD2SQ(error));
}
HSQOBJECT obj;
sq_getstackobj(vm, -1, &obj);
sq_pop(vm, 3);
if (StrEmpty(class_name)) return obj;
/* Now link the name the user wanted to our 'fake' class */
sq_pushobject(vm, parent);
sq_pushstring(vm, OTTD2SQ(class_name), -1);
sq_pushobject(vm, obj);
sq_newclass(vm, SQTrue);
sq_newslot(vm, -3, SQFalse);
sq_pop(vm, 1);
return obj;
}

View File

@@ -0,0 +1,135 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_controller.hpp The controller of the AI. */
#ifndef SCRIPT_CONTROLLER_HPP
#define SCRIPT_CONTROLLER_HPP
#include "../../core/string_compare_type.hpp"
#include <map>
/**
* 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:
/**
* 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.
*/
static uint GetTick();
/**
* Get the number of operations the AI may still execute this tick.
* @return The amount of operations left to execute.
* @note This number can go negative when certain uninteruptable
* operations are executed. The amount of operations that you go
* over the limit will be deducted from the next tick you would
* be allowed to run.
*/
static int GetOpsTillSuspend();
/**
* 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.
*/
static int GetSetting(const char *name);
/**
* Get the OpenTTD version of this executable. The version is formatted
* with the bits having the following meaning:
* 28-31 major version
* 24-27 minor version
* 20-23 build
* 19 1 if it is a release, 0 if it is not.
* 0-18 revision number; 0 when the revision is unknown.
* @return The version in newgrf format.
*/
static uint GetVersion();
/**
* 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 than 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);
/**
* Import a library.
* @param library The name of the library to import.
* @param class_name Under which name you want it to be available (or "" if you just want the returning object).
* @param version Which version you want specificly.
* @return The loaded library object. If class_name is set, it is also available (under the scope of the import) under that name.
* @note This command can be called from the global space, and does not need an instance.
*/
static HSQOBJECT Import(const char *library, const char *class_name, int version);
private:
typedef std::map<const char *, const char *, StringCompare> LoadedLibraryList; ///< The type for loaded libraries.
uint ticks; ///< The amount of ticks we're sleeping.
LoadedLibraryList loaded_library; ///< The libraries we loaded.
int loaded_library_count; ///< The amount of libraries.
/**
* Register all classes that are known inside the NoAI API.
*/
void RegisterClasses();
};
#endif /* SCRIPT_CONTROLLER_HPP */

View File

@@ -0,0 +1,55 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_date.cpp Implementation of AIDate. */
#include "../../stdafx.h"
#include "script_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);
}

View File

@@ -0,0 +1,69 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_date.hpp Everything to query and manipulate date related information. */
#ifndef SCRIPT_DATE_HPP
#define SCRIPT_DATE_HPP
#include "script_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:
/**
* 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 /* SCRIPT_DATE_HPP */

View File

@@ -0,0 +1,47 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_depotlist.cpp Implementation of AIDepotList and friends. */
#include "../../stdafx.h"
#include "script_depotlist.hpp"
#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) {
for (uint i = 0; i < st->airport.GetNumHangars(); i++) {
this->AddItem(st->airport.GetHangarTile(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);
}
}

View File

@@ -0,0 +1,30 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_depotlist.hpp List all the depots (you own). */
#ifndef SCRIPT_DEPOTLIST_HPP
#define SCRIPT_DEPOTLIST_HPP
#include "script_list.hpp"
#include "script_tile.hpp"
/**
* Creates a list of the locations of the depots (and hangars) of which you are the owner.
* @ingroup AIList
*/
class AIDepotList : public AIList {
public:
/**
* @param transport_type The type of transport to make a list of depots for.
*/
AIDepotList(AITile::TransportType transport_type);
};
#endif /* SCRIPT_DEPOTLIST_HPP */

View File

@@ -0,0 +1,250 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_engine.cpp Implementation of AIEngine. */
#include "../../stdafx.h"
#include "script_engine.hpp"
#include "script_cargo.hpp"
#include "../../company_func.h"
#include "../../company_base.h"
#include "../../strings_func.h"
#include "../../rail.h"
#include "../../engine_base.h"
#include "../../engine_func.h"
#include "../../articulated_vehicles.h"
#include "table/strings.h"
/* static */ bool AIEngine::IsValidEngine(EngineID engine_id)
{
const Engine *e = ::Engine::GetIfValid(engine_id);
return e != NULL && (::IsEngineBuildable(engine_id, e->type, _current_company) || ::Company::Get(_current_company)->group_all[e->type].num_engines[engine_id] > 0);
}
/* static */ bool AIEngine::IsBuildable(EngineID engine_id)
{
const Engine *e = ::Engine::GetIfValid(engine_id);
return e != NULL && ::IsEngineBuildable(engine_id, e->type, _current_company);
}
/* static */ 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;
CargoArray cap = ::GetCapacityOfArticulatedParts(engine_id);
CargoID most_cargo = CT_INVALID;
uint amount = 0;
for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
if (cap[cid] > amount) {
amount = cap[cid];
most_cargo = cid;
}
}
return most_cargo;
}
/* static */ bool AIEngine::CanRefitCargo(EngineID engine_id, CargoID cargo_id)
{
if (!IsValidEngine(engine_id)) return false;
if (!AICargo::IsValidCargo(cargo_id)) return false;
return HasBit(::GetUnionOfArticulatedRefitMasks(engine_id, true), cargo_id);
}
/* static */ bool AIEngine::CanPullCargo(EngineID engine_id, CargoID cargo_id)
{
if (!IsValidEngine(engine_id)) return false;
if (GetVehicleType(engine_id) != AIVehicle::VT_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;
const Engine *e = ::Engine::Get(engine_id);
switch (e->type) {
case VEH_ROAD:
case VEH_TRAIN: {
CargoArray capacities = GetCapacityOfArticulatedParts(engine_id);
for (CargoID c = 0; c < NUM_CARGO; c++) {
if (capacities[c] == 0) continue;
return capacities[c];
}
return -1;
}
case VEH_SHIP:
case VEH_AIRCRAFT:
return e->GetDisplayDefaultCapacity();
default: NOT_REACHED();
}
}
/* static */ int32 AIEngine::GetReliability(EngineID engine_id)
{
if (!IsValidEngine(engine_id)) return -1;
if (GetVehicleType(engine_id) == AIVehicle::VT_RAIL && IsWagon(engine_id)) return -1;
return ::ToPercent16(::Engine::Get(engine_id)->reliability);
}
/* static */ int32 AIEngine::GetMaxSpeed(EngineID engine_id)
{
if (!IsValidEngine(engine_id)) return -1;
const Engine *e = ::Engine::Get(engine_id);
int32 max_speed = e->GetDisplayMaxSpeed(); // km-ish/h
if (e->type == VEH_AIRCRAFT) max_speed /= _settings_game.vehicle.plane_speed;
return max_speed;
}
/* static */ Money AIEngine::GetPrice(EngineID engine_id)
{
if (!IsValidEngine(engine_id)) return -1;
return ::Engine::Get(engine_id)->GetCost();
}
/* static */ int32 AIEngine::GetMaxAge(EngineID engine_id)
{
if (!IsValidEngine(engine_id)) return -1;
if (GetVehicleType(engine_id) == AIVehicle::VT_RAIL && IsWagon(engine_id)) return -1;
return ::Engine::Get(engine_id)->GetLifeLengthInDays();
}
/* static */ Money AIEngine::GetRunningCost(EngineID engine_id)
{
if (!IsValidEngine(engine_id)) return -1;
return ::Engine::Get(engine_id)->GetRunningCost();
}
/* static */ int32 AIEngine::GetPower(EngineID engine_id)
{
if (!IsValidEngine(engine_id)) return -1;
if (GetVehicleType(engine_id) != AIVehicle::VT_RAIL && GetVehicleType(engine_id) != AIVehicle::VT_ROAD) return -1;
if (IsWagon(engine_id)) return -1;
return ::Engine::Get(engine_id)->GetPower();
}
/* static */ int32 AIEngine::GetWeight(EngineID engine_id)
{
if (!IsValidEngine(engine_id)) return -1;
if (GetVehicleType(engine_id) != AIVehicle::VT_RAIL && GetVehicleType(engine_id) != AIVehicle::VT_ROAD) return -1;
return ::Engine::Get(engine_id)->GetDisplayWeight();
}
/* static */ int32 AIEngine::GetMaxTractiveEffort(EngineID engine_id)
{
if (!IsValidEngine(engine_id)) return -1;
if (GetVehicleType(engine_id) != AIVehicle::VT_RAIL && GetVehicleType(engine_id) != AIVehicle::VT_ROAD) return -1;
if (IsWagon(engine_id)) return -1;
return ::Engine::Get(engine_id)->GetDisplayMaxTractiveEffort();
}
/* static */ int32 AIEngine::GetDesignDate(EngineID engine_id)
{
if (!IsValidEngine(engine_id)) return -1;
return ::Engine::Get(engine_id)->intro_date;
}
/* static */ AIVehicle::VehicleType AIEngine::GetVehicleType(EngineID engine_id)
{
if (!IsValidEngine(engine_id)) return AIVehicle::VT_INVALID;
switch (::Engine::Get(engine_id)->type) {
case VEH_ROAD: return AIVehicle::VT_ROAD;
case VEH_TRAIN: return AIVehicle::VT_RAIL;
case VEH_SHIP: return AIVehicle::VT_WATER;
case VEH_AIRCRAFT: return AIVehicle::VT_AIR;
default: NOT_REACHED();
}
}
/* static */ bool AIEngine::IsWagon(EngineID engine_id)
{
if (!IsValidEngine(engine_id)) return false;
if (GetVehicleType(engine_id) != AIVehicle::VT_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::VT_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::VT_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::VT_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::VT_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::VT_ROAD && GetVehicleType(engine_id) != AIVehicle::VT_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::VT_AIR) return AIAirport::PT_INVALID;
return (AIAirport::PlaneType)::AircraftVehInfo(engine_id)->subtype;
}

View File

@@ -0,0 +1,254 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_engine.hpp Everything to query and build engines. */
#ifndef SCRIPT_ENGINE_HPP
#define SCRIPT_ENGINE_HPP
#include "script_vehicle.hpp"
#include "script_rail.hpp"
#include "script_airport.hpp"
/**
* Class that handles all engine related functions.
*/
class AIEngine : public AIObject {
public:
/**
* Checks whether the given engine type is valid. An engine is valid if you
* have at least one vehicle of this engine or it's currently buildable.
* @param engine_id The engine to check.
* @return True if and only if the engine type is valid.
*/
static bool IsValidEngine(EngineID engine_id);
/**
* Checks whether the given engine type is buildable by you.
* @param engine_id The engine to check.
* @return True if and only if the engine type is buildable.
*/
static bool IsBuildable(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 char *GetName(EngineID engine_id);
/**
* Get the cargo-type of an engine. In case it can transport multiple cargos, it
* returns the first/main.
* @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.
* In case of articulated vehicles the function decides whether at least one
* part can carry the cargo.
* @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::VT_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 multiple cargos, it
* returns the first/main.
* @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).
* @pre GetVehicleType(engine_id) != AIVehicle::VT_TRAIN || !IsWagon(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).
* @pre GetVehicleType(engine_id) != AIVehicle::VT_TRAIN || !IsWagon(engine_id).
* @return The maximum speed the engine has.
* @note The speed is in OpenTTD's internal speed unit.
* This is mph / 1.6, which is roughly km/h.
* To get km/h multiply this number by 1.00584.
*/
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 365 to get per day.
*/
static Money GetRunningCost(EngineID engine_id);
/**
* Get the power of an engine.
* @param engine_id The engine to get the power of.
* @pre IsValidEngine(engine_id).
* @pre (GetVehicleType(engine_id) == AIVehicle::VT_RAIL || GetVehicleType(engine_id) == AIVehicle::VT_ROAD) && !IsWagon(engine_id).
* @return The power of the engine in hp.
*/
static int32 GetPower(EngineID engine_id);
/**
* Get the weight of an engine.
* @param engine_id The engine to get the weight of.
* @pre IsValidEngine(engine_id).
* @pre (GetVehicleType(engine_id) == AIVehicle::VT_RAIL || GetVehicleType(engine_id) == AIVehicle::VT_ROAD).
* @return The weight of the engine in metric tons.
*/
static int32 GetWeight(EngineID engine_id);
/**
* Get the maximum tractive effort of an engine.
* @param engine_id The engine to get the maximum tractive effort of.
* @pre IsValidEngine(engine_id).
* @pre (GetVehicleType(engine_id) == AIVehicle::VT_RAIL || GetVehicleType(engine_id) == AIVehicle::VT_ROAD) && !IsWagon(engine_id).
* @return The maximum tractive effort of the engine in kN.
*/
static int32 GetMaxTractiveEffort(EngineID engine_id);
/**
* Get the date this engine was designed.
* @param engine_id The engine to get the design date of.
* @pre IsValidEngine(engine_id).
* @return The date this engine was designed.
*/
static int32 GetDesignDate(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::VT_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::VT_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::VT_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::VT_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::VT_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::VT_ROAD || GetVehicleType(engine_id) == AIVehicle::VT_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::VT_AIR.
* @return The PlaneType the engine has.
*/
static AIAirport::PlaneType GetPlaneType(EngineID engine_id);
};
#endif /* SCRIPT_ENGINE_HPP */

View File

@@ -0,0 +1,24 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_enginelist.cpp Implementation of AIEngineList and friends. */
#include "../../stdafx.h"
#include "script_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);
}
}

View File

@@ -0,0 +1,30 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_enginelist.hpp List all the engines. */
#ifndef SCRIPT_ENGINELIST_HPP
#define SCRIPT_ENGINELIST_HPP
#include "script_list.hpp"
#include "script_vehicle.hpp"
/**
* Create a list of engines based on a vehicle type.
* @ingroup AIList
*/
class AIEngineList : public AIList {
public:
/**
* @param vehicle_type The type of vehicle to make a list of engines for.
*/
AIEngineList(AIVehicle::VehicleType vehicle_type);
};
#endif /* SCRIPT_ENGINELIST_HPP */

View File

@@ -0,0 +1,68 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_error.cpp Implementation of AIError. */
#include "../../stdafx.h"
#include "script_error.hpp"
#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 */ char *AIError::GetLastErrorString()
{
return strdup((*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);
}

View File

@@ -0,0 +1,179 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_error.hpp Everything to query errors. */
#ifndef SCRIPT_ERROR_HPP
#define SCRIPT_ERROR_HPP
#include "script_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:
/**
* 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.
ERR_CAT_WAYPOINT, ///< Error messages related to building / maintaining waypoints.
/**
* 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 occurred 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_ERROR_NOT_ENOUGH_CASH_REQUIRES_CURRENCY]
/** Local authority won't allow the previous action */
ERR_LOCAL_AUTHORITY_REFUSES, // [STR_ERROR_LOCAL_AUTHORITY_REFUSES_TO_ALLOW_THIS]
/** The piece of infrastructure you tried to build is already in place */
ERR_ALREADY_BUILT, // [STR_ERROR_ALREADY_BUILT, STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST]
/** Area isn't clear, try to demolish the building on it */
ERR_AREA_NOT_CLEAR, // [STR_ERROR_BUILDING_MUST_BE_DEMOLISHED, STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST, STR_ERROR_MUST_DEMOLISH_RAILROAD, STR_ERROR_MUST_DEMOLISH_AIRPORT_FIRST, STR_ERROR_MUST_DEMOLISH_CARGO_TRAM_STATION_FIRST, STR_ERROR_MUST_DEMOLISH_TRUCK_STATION_FIRST, STR_ERROR_MUST_DEMOLISH_PASSENGER_TRAM_STATION_FIRST, STR_ERROR_MUST_DEMOLISH_BUS_STATION_FIRST, STR_ERROR_BUOY_IN_THE_WAY, STR_ERROR_MUST_DEMOLISH_DOCK_FIRST, STR_ERROR_GENERIC_OBJECT_IN_THE_WAY, STR_ERROR_COMPANY_HEADQUARTERS_IN, STR_ERROR_OBJECT_IN_THE_WAY, STR_ERROR_MUST_REMOVE_ROAD_FIRST, STR_ERROR_MUST_REMOVE_RAILROAD_TRACK, STR_ERROR_MUST_DEMOLISH_BRIDGE_FIRST, STR_ERROR_MUST_DEMOLISH_TUNNEL_FIRST, STR_ERROR_EXCAVATION_WOULD_DAMAGE]
/** Area / property is owned by another company */
ERR_OWNED_BY_ANOTHER_COMPANY, // [STR_ERROR_AREA_IS_OWNED_BY_ANOTHER, STR_ERROR_OWNED_BY]
/** The name given is not unique for the object type */
ERR_NAME_IS_NOT_UNIQUE, // [STR_ERROR_NAME_MUST_BE_UNIQUE]
/** The building you want to build requires flat land */
ERR_FLAT_LAND_REQUIRED, // [STR_ERROR_FLAT_LAND_REQUIRED]
/** Land is sloped in the wrong direction for this build action */
ERR_LAND_SLOPED_WRONG, // [STR_ERROR_LAND_SLOPED_IN_WRONG_DIRECTION]
/** A vehicle is in the way */
ERR_VEHICLE_IN_THE_WAY, // [STR_ERROR_TRAIN_IN_THE_WAY, STR_ERROR_ROAD_VEHICLE_IN_THE_WAY, STR_ERROR_SHIP_IN_THE_WAY, STR_ERROR_AIRCRAFT_IN_THE_WAY]
/** Site is unsuitable */
ERR_SITE_UNSUITABLE, // [STR_ERROR_SITE_UNSUITABLE]
/** Too close to the edge of the map */
ERR_TOO_CLOSE_TO_EDGE, // [STR_ERROR_TOO_CLOSE_TO_EDGE_OF_MAP]
/** Station is too spread out */
ERR_STATION_TOO_SPREAD_OUT, // [STR_ERROR_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 char *GetLastErrorString();
#ifndef EXPORT_SKIP
/**
* 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 its 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 its 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);
#endif /* EXPORT_SKIP */
private:
typedef std::map<StringID, AIErrorType> AIErrorMap; ///< The type for mapping between error (internal OpenTTD) StringID to the AI error type.
typedef std::map<AIErrorType, const char *> AIErrorMapString; ///< The type for mapping between error type and textual representation.
static AIErrorMap error_map; ///< The mapping between error (internal OpenTTD) StringID to the AI error type.
static AIErrorMapString error_map_string; ///< The mapping between error type and textual representation.
};
#endif /* SCRIPT_ERROR_HPP */

View File

@@ -0,0 +1,72 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_event.cpp Implementation of AIEvent. */
#include "../../stdafx.h"
#include "script_event_types.hpp"
#include <queue>
/** The queue of events for an AI. */
struct AIEventData {
std::queue<AIEvent *> stack; ///< The actual queue.
};
/* 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);
}

View File

@@ -0,0 +1,114 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_event.hpp Everything to handle events from the game. */
#ifndef SCRIPT_EVENT_HPP
#define SCRIPT_EVENT_HPP
#include "script_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:
/**
* 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_ASK_MERGER,
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,
AI_ET_DISASTER_ZEPPELINER_CRASHED,
AI_ET_DISASTER_ZEPPELINER_CLEARED,
AI_ET_TOWN_FOUNDED,
};
/**
* 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:
/**
* 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();
#ifndef EXPORT_SKIP
/**
* Insert an event to the queue for the company.
* @param event The event to insert.
* @note DO NOT CALL YOURSELF; leave it to the internal AI programming.
*/
static void InsertEvent(AIEvent *event);
/**
* Free the event pointer.
* @note DO NOT CALL YOURSELF; leave it to the internal AI programming.
*/
static void FreeEventPointer();
#endif /* EXPORT_SKIP */
private:
/**
* Create the event pointer.
*/
static void CreateEventPointer();
};
#endif /* SCRIPT_EVENT_HPP */

View File

@@ -0,0 +1,121 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_event_types.cpp Implementation of all EventTypes. */
#include "../../stdafx.h"
#include "script_event_types.hpp"
#include "script_vehicle.hpp"
#include "../../command_type.h"
#include "../../strings_func.h"
#include "../../settings_type.h"
#include "../../engine_base.h"
#include "../../articulated_vehicles.h"
#include "table/strings.h"
bool AIEventEnginePreview::IsEngineValid() const
{
const Engine *e = ::Engine::GetIfValid(this->engine);
return e != NULL && e->IsEnabled();
}
char *AIEventEnginePreview::GetName()
{
if (!this->IsEngineValid()) return NULL;
static const int len = 64;
char *engine_name = MallocT<char>(len);
::SetDParam(0, this->engine);
::GetString(engine_name, STR_ENGINE_NAME, &engine_name[len - 1]);
return engine_name;
}
CargoID AIEventEnginePreview::GetCargoType()
{
if (!this->IsEngineValid()) return CT_INVALID;
CargoArray cap = ::GetCapacityOfArticulatedParts(this->engine);
CargoID most_cargo = CT_INVALID;
uint amount = 0;
for (CargoID cid = 0; cid < NUM_CARGO; cid++) {
if (cap[cid] > amount) {
amount = cap[cid];
most_cargo = cid;
}
}
return most_cargo;
}
int32 AIEventEnginePreview::GetCapacity()
{
if (!this->IsEngineValid()) return -1;
const Engine *e = ::Engine::Get(this->engine);
switch (e->type) {
case VEH_ROAD:
case VEH_TRAIN: {
CargoArray capacities = GetCapacityOfArticulatedParts(this->engine);
for (CargoID c = 0; c < NUM_CARGO; c++) {
if (capacities[c] == 0) continue;
return capacities[c];
}
return -1;
}
case VEH_SHIP:
case VEH_AIRCRAFT:
return e->GetDisplayDefaultCapacity();
default: NOT_REACHED();
}
}
int32 AIEventEnginePreview::GetMaxSpeed()
{
if (!this->IsEngineValid()) return -1;
const Engine *e = ::Engine::Get(this->engine);
int32 max_speed = e->GetDisplayMaxSpeed(); // km-ish/h
if (e->type == VEH_AIRCRAFT) max_speed /= _settings_game.vehicle.plane_speed;
return max_speed;
}
Money AIEventEnginePreview::GetPrice()
{
if (!this->IsEngineValid()) return -1;
return ::Engine::Get(this->engine)->GetCost();
}
Money AIEventEnginePreview::GetRunningCost()
{
if (!this->IsEngineValid()) return -1;
return ::Engine::Get(this->engine)->GetRunningCost();
}
int32 AIEventEnginePreview::GetVehicleType()
{
if (!this->IsEngineValid()) return AIVehicle::VT_INVALID;
switch (::Engine::Get(this->engine)->type) {
case VEH_ROAD: return AIVehicle::VT_ROAD;
case VEH_TRAIN: return AIVehicle::VT_RAIL;
case VEH_SHIP: return AIVehicle::VT_WATER;
case VEH_AIRCRAFT: return AIVehicle::VT_AIR;
default: NOT_REACHED();
}
}
bool AIEventEnginePreview::AcceptPreview()
{
if (!this->IsEngineValid()) return false;
return AIObject::DoCommand(0, this->engine, 0, CMD_WANT_ENGINE_PREVIEW);
}
bool AIEventCompanyAskMerger::AcceptMerger()
{
return AIObject::DoCommand(0, this->owner, 0, CMD_BUY_COMPANY);
}

View File

@@ -0,0 +1,778 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_event_types.hpp The detailed types of all events. */
#ifndef SCRIPT_EVENT_TYPES_HPP
#define SCRIPT_EVENT_TYPES_HPP
#include "script_event.hpp"
#include "script_company.hpp"
/**
* Event Vehicle Crash, indicating a vehicle of yours is crashed.
* It contains the crash site, the crashed vehicle and the reason for the crash.
*/
class AIEventVehicleCrashed : public AIEvent {
public:
/**
* The reasons for vehicle crashes
*/
enum CrashReason {
CRASH_TRAIN, ///< Two trains collided
CRASH_RV_LEVEL_CROSSING, ///< Road vehicle got under a train
CRASH_RV_UFO, ///< Road vehicle got under a landing ufo
CRASH_PLANE_LANDING, ///< Plane crashed on landing
CRASH_AIRCRAFT_NO_AIRPORT, ///< Aircraft crashed after it found not a single airport for landing
CRASH_FLOODED, ///< Vehicle was flooded
};
/**
* @param vehicle The vehicle that crashed.
* @param crash_site Where the vehicle crashed.
* @param crash_reason The reason why the vehicle crashed.
*/
AIEventVehicleCrashed(VehicleID vehicle, TileIndex crash_site, CrashReason crash_reason) :
AIEvent(AI_ET_VEHICLE_CRASHED),
crash_site(crash_site),
vehicle(vehicle),
crash_reason(crash_reason)
{}
/**
* 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 this->vehicle; }
/**
* Find the tile the vehicle crashed.
* @return The crash site.
*/
TileIndex GetCrashSite() { return this->crash_site; }
/**
* Get the reason for crashing
* @return The reason for crashing
*/
CrashReason GetCrashReason() { return this->crash_reason; }
private:
TileIndex crash_site; ///< The location of the crash.
VehicleID vehicle; ///< The crashed vehicle.
CrashReason crash_reason; ///< The reason for crashing.
};
/**
* Event Subsidy Offered, indicating someone offered a subsidy.
*/
class AIEventSubsidyOffer : public AIEvent {
public:
/**
* @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 this->subsidy_id; }
private:
SubsidyID subsidy_id; ///< The subsidy that got offered.
};
/**
* Event Subsidy Offer Expired, indicating a subsidy will no longer be awarded.
*/
class AIEventSubsidyOfferExpired : public AIEvent {
public:
/**
* @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 this->subsidy_id; }
private:
SubsidyID subsidy_id; ///< The subsidy offer that expired.
};
/**
* Event Subidy Awarded, indicating a subsidy is awarded to some company.
*/
class AIEventSubsidyAwarded : public AIEvent {
public:
/**
* @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 this->subsidy_id; }
private:
SubsidyID subsidy_id; ///< The subsidy that was awared.
};
/**
* Event Subsidy Expired, indicating a route that was once subsidized no longer is.
*/
class AIEventSubsidyExpired : public AIEvent {
public:
/**
* @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 this->subsidy_id; }
private:
SubsidyID subsidy_id; ///< The subsidy that expired.
};
/**
* 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:
/**
* @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.
*/
char *GetName();
/**
* Get the cargo-type of the offered engine. In case it can transport multiple cargos, it
* returns the first/main.
* @return The cargo-type of the engine.
*/
CargoID GetCargoType();
/**
* Get the capacity of the offered engine. In case it can transport multiple cargos, it
* returns the first/main.
* @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 OpenTTD's internal speed unit.
* This is mph / 1.6, which is roughly km/h.
* To get km/h multiply this number by 1.00584.
*/
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 365 to get per day.
*/
Money GetRunningCost();
/**
* Get the type of the offered engine.
* @return The type the engine has.
*/
#ifdef DOXYGEN_AI_DOCS
AIVehicle::VehicleType GetVehicleType();
#else
int32 GetVehicleType();
#endif
/**
* Accept the engine preview.
* @return True when the accepting succeeded.
*/
bool AcceptPreview();
private:
EngineID engine; ///< The engine the preview is for.
/**
* Check whether the engine from this preview is still valid.
* @return True iff the engine is still valid.
*/
bool IsEngineValid() const;
};
/**
* Event Company New, indicating a new company has been created.
*/
class AIEventCompanyNew : public AIEvent {
public:
/**
* @param owner The new company.
*/
AIEventCompanyNew(Owner owner) :
AIEvent(AI_ET_COMPANY_NEW),
owner((AICompany::CompanyID)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 this->owner; }
private:
AICompany::CompanyID owner; ///< The new company.
};
/**
* Event Company In Trouble, indicating a company is in trouble and might go
* bankrupt soon.
*/
class AIEventCompanyInTrouble : public AIEvent {
public:
/**
* @param owner The company that is in trouble.
*/
AIEventCompanyInTrouble(Owner owner) :
AIEvent(AI_ET_COMPANY_IN_TROUBLE),
owner((AICompany::CompanyID)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 this->owner; }
private:
AICompany::CompanyID owner; ///< The company that is in trouble.
};
/**
* Event Company Ask Merger, indicating a company can be bought (cheaply) by you.
*/
class AIEventCompanyAskMerger : public AIEvent {
public:
/**
* @param owner The company that can be bough.
* @param value The value/costs of buying the company.
*/
AIEventCompanyAskMerger(Owner owner, int32 value) :
AIEvent(AI_ET_COMPANY_ASK_MERGER),
owner((AICompany::CompanyID)owner),
value(value)
{}
/**
* Convert an AIEvent to the real instance.
* @param instance The instance to convert.
* @return The converted instance.
*/
static AIEventCompanyAskMerger *Convert(AIEvent *instance) { return (AIEventCompanyAskMerger *)instance; }
/**
* Get the CompanyID of the company that can be bought.
* @return The CompanyID of the company that can be bought.
* @note If the company is bought this will become invalid.
*/
AICompany::CompanyID GetCompanyID() { return this->owner; }
/**
* Get the value of the new company.
* @return The value of the new company.
*/
int32 GetValue() { return this->value; }
/**
* Take over the company for this merger.
* @return true if the merger was a success.
*/
bool AcceptMerger();
private:
AICompany::CompanyID owner; ///< The company that is in trouble.
int32 value; ///< The value of the company, i.e. the amount you would pay.
};
/**
* Event Company Merger, indicating a company has been bought by another
* company.
*/
class AIEventCompanyMerger : public AIEvent {
public:
/**
* @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)old_owner),
new_owner((AICompany::CompanyID)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 COMPANY_COMPANY. It's
* only usefull if you're keeping track of company's yourself.
*/
AICompany::CompanyID GetOldCompanyID() { return this->old_owner; }
/**
* Get the CompanyID of the new owner.
* @return The CompanyID of the new owner.
*/
AICompany::CompanyID GetNewCompanyID() { return this->new_owner; }
private:
AICompany::CompanyID old_owner; ///< The company that ended to exist.
AICompany::CompanyID new_owner; ///< The company that's the end result of the merger.
};
/**
* Event Company Bankrupt, indicating a company has gone bankrupt.
*/
class AIEventCompanyBankrupt : public AIEvent {
public:
/**
* @param owner The company that has gone bankrupt.
*/
AIEventCompanyBankrupt(Owner owner) :
AIEvent(AI_ET_COMPANY_BANKRUPT),
owner((AICompany::CompanyID)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 this->owner; }
private:
AICompany::CompanyID owner; ///< The company that has gone bankrupt.
};
/**
* Event Vehicle Lost, indicating a vehicle can't find its way to its destination.
*/
class AIEventVehicleLost : public AIEvent {
public:
/**
* @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 this->vehicle_id; }
private:
VehicleID vehicle_id; ///< The vehicle that is lost.
};
/**
* Event VehicleWaitingInDepot, indicating a vehicle has arrived a depot and is now waiting there.
*/
class AIEventVehicleWaitingInDepot : public AIEvent {
public:
/**
* @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 this->vehicle_id; }
private:
VehicleID vehicle_id; ///< The vehicle that is waiting in the depot.
};
/**
* Event Vehicle Unprofitable, indicating a vehicle lost money last year.
*/
class AIEventVehicleUnprofitable : public AIEvent {
public:
/**
* @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 this->vehicle_id; }
private:
VehicleID vehicle_id; ///< The vehicle that is unprofitable.
};
/**
* Event Industry Open, indicating a new industry has been created.
*/
class AIEventIndustryOpen : public AIEvent {
public:
/**
* @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 this->industry_id; }
private:
IndustryID industry_id; ///< The industry that opened.
};
/**
* Event Industry Close, indicating an industry is going to be closed.
*/
class AIEventIndustryClose : public AIEvent {
public:
/**
* @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 this->industry_id; }
private:
IndustryID industry_id; ///< The industry that closed.
};
/**
* Event Engine Available, indicating a new engine is available.
*/
class AIEventEngineAvailable : public AIEvent {
public:
/**
* @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 this->engine; }
private:
EngineID engine; ///< The engine that became available.
};
/**
* Event Station First Vehicle, indicating a station has been visited by a vehicle for the first time.
*/
class AIEventStationFirstVehicle : public AIEvent {
public:
/**
* @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 this->station; }
/**
* Get the VehicleID of the first vehicle.
* @return The VehicleID of the first vehicle.
*/
VehicleID GetVehicleID() { return this->vehicle; }
private:
StationID station; ///< The station the vehicle arived at.
VehicleID vehicle; ///< The vehicle that arrived at the station.
};
/**
* Event Disaster Zeppeliner Crashed, indicating a zeppeliner has crashed on an airport and is blocking the runway.
*/
class AIEventDisasterZeppelinerCrashed : public AIEvent {
public:
/**
* @param station The station containing the affected airport
*/
AIEventDisasterZeppelinerCrashed(StationID station) :
AIEvent(AI_ET_DISASTER_ZEPPELINER_CRASHED),
station(station)
{}
/**
* Convert an AIEvent to the real instance.
* @param instance The instance to convert.
* @return The converted instance.
*/
static AIEventDisasterZeppelinerCrashed *Convert(AIEvent *instance) { return (AIEventDisasterZeppelinerCrashed *)instance; }
/**
* Get the StationID of the station containing the affected airport.
* @return The StationID of the station containing the affected airport.
*/
StationID GetStationID() { return this->station; }
private:
StationID station; ///< The station the zeppeliner crashed.
};
/**
* Event Disaster Zeppeliner Cleared, indicating a previously crashed zeppeliner has been removed, and the airport is operating again.
*/
class AIEventDisasterZeppelinerCleared : public AIEvent {
public:
/**
* @param station The station containing the affected airport
*/
AIEventDisasterZeppelinerCleared(StationID station) :
AIEvent(AI_ET_DISASTER_ZEPPELINER_CLEARED),
station(station)
{}
/**
* Convert an AIEvent to the real instance.
* @param instance The instance to convert.
* @return The converted instance.
*/
static AIEventDisasterZeppelinerCleared *Convert(AIEvent *instance) { return (AIEventDisasterZeppelinerCleared *)instance; }
/**
* Get the StationID of the station containing the affected airport.
* @return The StationID of the station containing the affected airport.
*/
StationID GetStationID() { return this->station; }
private:
StationID station; ///< The station the zeppeliner crashed.
};
/**
* Event Town Founded, indicating a new town has been created.
*/
class AIEventTownFounded : public AIEvent {
public:
/**
* @param town The town that was created.
*/
AIEventTownFounded(TownID town) :
AIEvent(AI_ET_TOWN_FOUNDED),
town(town)
{}
/**
* Convert an AIEvent to the real instance.
* @param instance The instance to convert.
* @return The converted instance.
*/
static AIEventTownFounded *Convert(AIEvent *instance) { return (AIEventTownFounded *)instance; }
/**
* Get the TownID of the town.
* @return The TownID of the town that was created.
*/
TownID GetTownID() { return this->town; }
private:
TownID town; ///< The town that got founded.
};
#endif /* SCRIPT_EVENT_TYPES_HPP */

View File

@@ -0,0 +1,41 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_execmode.cpp Implementation of AIExecMode. */
#include "../../stdafx.h"
#include "script_execmode.hpp"
#include "../../company_base.h"
#include "../../company_func.h"
#include "../../ai/ai_instance.hpp"
bool AIExecMode::ModeProc()
{
/* 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()
{
if (this->GetDoCommandModeInstance() != this) {
/* Ignore this error if the AI already died. */
if (!AIObject::GetActiveInstance()->IsDead()) {
throw AI_FatalError("AIExecMode object was removed while it was not the latest AI*Mode object created.");
}
}
this->SetDoCommandMode(this->last_mode, this->last_instance);
}

View File

@@ -0,0 +1,50 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_execmode.hpp Switch the AI to Execute Mode. */
#ifndef SCRIPT_EXECMODE_HPP
#define SCRIPT_EXECMODE_HPP
#include "script_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 {
private:
AIModeProc *last_mode; ///< The previous mode we were in.
AIObject *last_instance; ///< The previous instace of the mode.
protected:
/**
* The callback proc for Execute mode.
*/
static bool ModeProc();
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 /* SCRIPT_EXECMODE_HPP */

View File

@@ -0,0 +1,45 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_gamesettings.cpp Implementation of AIGameSettings. */
#include "../../stdafx.h"
#include "script_gamesettings.hpp"
#include "../../settings_internal.h"
/* static */ bool AIGameSettings::IsValid(const char *setting)
{
uint i;
const SettingDesc *sd = GetSettingFromName(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 = GetSettingFromName(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::VT_RAIL: return _settings_game.ai.ai_disable_veh_train;
case AIVehicle::VT_ROAD: return _settings_game.ai.ai_disable_veh_roadveh;
case AIVehicle::VT_WATER: return _settings_game.ai.ai_disable_veh_ship;
case AIVehicle::VT_AIR: return _settings_game.ai.ai_disable_veh_aircraft;
default: return true;
}
}

View File

@@ -0,0 +1,71 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_gamesettings.hpp Everything to read game settings. */
#ifndef SCRIPT_GAMESETTINGS_HPP
#define SCRIPT_GAMESETTINGS_HPP
#include "script_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_settings 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:
/**
* 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 its 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 its 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 /* SCRIPT_GAMESETTINGS_HPP */

View File

@@ -0,0 +1,134 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_group.cpp Implementation of AIGroup. */
#include "../../stdafx.h"
#include "script_group.hpp"
#include "script_engine.hpp"
#include "../../ai/ai_instance.hpp"
#include "../../company_func.h"
#include "../../group.h"
#include "../../string_func.h"
#include "../../strings_func.h"
#include "../../autoreplace_func.h"
#include "../../settings_func.h"
#include "table/strings.h"
/* static */ bool AIGroup::IsValidGroup(GroupID group_id)
{
const Group *g = ::Group::GetIfValid(group_id);
return g != NULL && g->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 GROUP_INVALID;
/* 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::VT_INVALID;
return (AIVehicle::VehicleType)((::VehicleType)::Group::Get(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, ::Utf8StringLength(name) < MAX_LENGTH_GROUP_NAME_CHARS, AIError::ERR_PRECONDITION_STRING_TOO_LONG);
return AIObject::DoCommand(0, group_id, 0, CMD_RENAME_GROUP, name);
}
/* static */ 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 ::Group::Get(group_id)->replace_protection;
}
/* static */ int32 AIGroup::GetNumEngines(GroupID group_id, EngineID engine_id)
{
if (!IsValidGroup(group_id) && group_id != GROUP_DEFAULT && group_id != GROUP_ALL) 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 == GROUP_DEFAULT);
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, ::GetCompanySettingIndex("company.renew_keep_length"), enable_removal ? 1 : 0, CMD_CHANGE_COMPANY_SETTING);
}
/* static */ bool AIGroup::HasWagonRemoval()
{
return ::Company::Get(_current_company)->settings.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 == GROUP_DEFAULT || group_id == GROUP_ALL);
EnforcePrecondition(false, AIEngine::IsBuildable(engine_id_new));
return AIObject::DoCommand(0, 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 != GROUP_DEFAULT && group_id != GROUP_ALL) return ::INVALID_ENGINE;
return ::EngineReplacementForCompany(Company::Get(_current_company), engine_id, group_id);
}
/* static */ bool AIGroup::StopAutoReplace(GroupID group_id, EngineID engine_id)
{
EnforcePrecondition(false, IsValidGroup(group_id) || group_id == GROUP_DEFAULT || group_id == GROUP_ALL);
return AIObject::DoCommand(0, group_id << 16, (::INVALID_ENGINE << 16) | engine_id, CMD_SET_AUTOREPLACE);
}

View File

@@ -0,0 +1,175 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_group.hpp Everything to put vehicles into groups. */
#ifndef SCRIPT_GROUP_HPP
#define SCRIPT_GROUP_HPP
#include "script_vehicle.hpp"
/**
* Class that handles all group related functions.
*/
class AIGroup : public AIObject {
public:
/**
* 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). */
GROUP_ALL = 0xFFFD, ///< All vehicles are in this group.
GROUP_DEFAULT = 0xFFFE, ///< Vehicles not put in any other group are in this one.
GROUP_INVALID = 0xFFFF, ///< An invalid group id.
};
/**
* Checks whether the given group is valid.
* @param group_id The group to check.
* @pre group_id != GROUP_DEFAULT && group_id != GROUP_ALL.
* @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 GROUP_DEFAULT.
* @param group_id The group to delete.
* @pre IsValidGroup(group_id).
* @return True if and only if the group was successfully 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 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 successfully 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 == GROUP_ALL || group_id == GROUP_DEFAULT.
* @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 == GROUP_DEFAULT.
* @pre AIVehicle::IsValidVehicle(vehicle_id).
* @return True if and only if the vehicle was successfully 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 GROUP_DEFAULT. 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 successfully 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 == GROUP_DEFAULT || group_id == GROUP_ALL.
* @pre AIEngine.IsBuildable(engine_id_new).
* @return True if and if the replacing was successfully started.
* @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 == GROUP_DEFAULT || group_id == GROUP_ALL.
* @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 == GROUP_DEFAULT || group_id == GROUP_ALL.
* @return True if and if the replacing was successfully stopped.
*/
static bool StopAutoReplace(GroupID group_id, EngineID engine_id);
};
#endif /* SCRIPT_GROUP_HPP */

View File

@@ -0,0 +1,23 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_grouplist.cpp Implementation of AIGroupList and friends. */
#include "../../stdafx.h"
#include "script_grouplist.hpp"
#include "../../company_func.h"
#include "../../group.h"
AIGroupList::AIGroupList()
{
Group *g;
FOR_ALL_GROUPS(g) {
if (g->owner == _current_company) this->AddItem(g->index);
}
}

View File

@@ -0,0 +1,27 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_grouplist.hpp List all the groups (you own). */
#ifndef SCRIPT_GROUPLIST_HPP
#define SCRIPT_GROUPLIST_HPP
#include "script_list.hpp"
/**
* Creates a list of groups of which you are the owner.
* @note Neither AIGroup::GROUP_ALL nor AIGroup::GROUP_DEFAULT is in this list.
* @ingroup AIList
*/
class AIGroupList : public AIList {
public:
AIGroupList();
};
#endif /* SCRIPT_GROUPLIST_HPP */

View File

@@ -0,0 +1,212 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_industry.cpp Implementation of AIIndustry. */
#include "../../stdafx.h"
#include "script_industry.hpp"
#include "script_cargo.hpp"
#include "script_map.hpp"
#include "../../industry.h"
#include "../../strings_func.h"
#include "../../station_base.h"
#include "../../newgrf_industries.h"
#include "table/strings.h"
/* static */ int32 AIIndustry::GetIndustryCount()
{
return (int32)::Industry::GetNumItems();
}
/* static */ bool AIIndustry::IsValidIndustry(IndustryID industry_id)
{
return ::Industry::IsValidID(industry_id);
}
/* static */ IndustryID AIIndustry::GetIndustryID(TileIndex tile)
{
if (!::IsValidTile(tile) || !::IsTileType(tile, MP_INDUSTRY)) return INVALID_INDUSTRY;
return ::GetIndustryIndex(tile);
}
/* static */ 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_NAME, &industry_name[len - 1]);
return industry_name;
}
/* static */ AIIndustry::CargoAcceptState AIIndustry::IsCargoAccepted(IndustryID industry_id, CargoID cargo_id)
{
if (!IsValidIndustry(industry_id)) return CAS_NOT_ACCEPTED;
if (!AICargo::IsValidCargo(cargo_id)) return CAS_NOT_ACCEPTED;
Industry *i = ::Industry::Get(industry_id);
for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
if (i->accepts_cargo[j] == cargo_id) {
if (IndustryTemporarilyRefusesCargo(i, cargo_id)) return CAS_TEMP_REFUSED;
return CAS_ACCEPTED;
}
}
return CAS_NOT_ACCEPTED;
}
/* 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 = ::Industry::Get(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 = ::Industry::Get(industry_id);
for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
if (i->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 = ::Industry::Get(industry_id);
for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
if (i->produced_cargo[j] == cargo_id) return i->last_month_transported[j];
}
return -1;
}
/* static */ int32 AIIndustry::GetLastMonthTransportedPercentage(IndustryID industry_id, CargoID cargo_id)
{
if (!IsValidIndustry(industry_id)) return -1;
if (!AICargo::IsValidCargo(cargo_id)) return -1;
const Industry *i = ::Industry::Get(industry_id);
for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
if (i->produced_cargo[j] == cargo_id) return ::ToPercent8(i->last_month_pct_transported[j]);
}
return -1;
}
/* static */ TileIndex AIIndustry::GetLocation(IndustryID industry_id)
{
if (!IsValidIndustry(industry_id)) return INVALID_TILE;
return ::Industry::Get(industry_id)->location.tile;
}
/* static */ int32 AIIndustry::GetAmountOfStationsAround(IndustryID industry_id)
{
if (!IsValidIndustry(industry_id)) return -1;
Industry *ind = ::Industry::Get(industry_id);
StationList stations;
::FindStationsAroundTiles(ind->location, &stations);
return (int32)stations.Length();
}
/* 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(::Industry::Get(industry_id)->type)->behaviour & INDUSTRYBEH_BUILT_ONWATER) != 0;
}
/* static */ bool AIIndustry::HasHeliport(IndustryID industry_id)
{
if (!IsValidIndustry(industry_id)) return false;
return (::GetIndustrySpec(::Industry::Get(industry_id)->type)->behaviour & INDUSTRYBEH_AI_AIRSHIP_ROUTES) != 0;
}
/* static */ TileIndex AIIndustry::GetHeliportLocation(IndustryID industry_id)
{
if (!IsValidIndustry(industry_id)) return INVALID_TILE;
if (!HasHeliport(industry_id)) return INVALID_TILE;
const Industry *ind = ::Industry::Get(industry_id);
TILE_AREA_LOOP(tile_cur, ind->location) {
if (IsTileType(tile_cur, MP_STATION) && IsOilRig(tile_cur)) {
return tile_cur;
}
}
return INVALID_TILE;
}
/* static */ bool AIIndustry::HasDock(IndustryID industry_id)
{
if (!IsValidIndustry(industry_id)) return false;
return (::GetIndustrySpec(::Industry::Get(industry_id)->type)->behaviour & INDUSTRYBEH_AI_AIRSHIP_ROUTES) != 0;
}
/* static */ TileIndex AIIndustry::GetDockLocation(IndustryID industry_id)
{
if (!IsValidIndustry(industry_id)) return INVALID_TILE;
if (!HasDock(industry_id)) return INVALID_TILE;
const Industry *ind = ::Industry::Get(industry_id);
TILE_AREA_LOOP(tile_cur, ind->location) {
if (IsTileType(tile_cur, MP_STATION) && IsOilRig(tile_cur)) {
return tile_cur;
}
}
return INVALID_TILE;
}
/* static */ IndustryType AIIndustry::GetIndustryType(IndustryID industry_id)
{
if (!IsValidIndustry(industry_id)) return INVALID_INDUSTRYTYPE;
return ::Industry::Get(industry_id)->type;
}

View File

@@ -0,0 +1,202 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_industry.hpp Everything to query and build industries. */
#ifndef SCRIPT_INDUSTRY_HPP
#define SCRIPT_INDUSTRY_HPP
#include "script_object.hpp"
/**
* Class that handles all industry related functions.
*/
class AIIndustry : public AIObject {
public:
/** Ways for an industry to accept a cargo. */
enum CargoAcceptState {
CAS_NOT_ACCEPTED, ///< The CargoID is not accepted by this industry.
CAS_ACCEPTED, ///< The industry currently accepts this CargoID.
CAS_TEMP_REFUSED, ///< The industry temporarily refuses to accept this CargoID but may do so again in the future.
};
/**
* Gets the number of industries.
* @return The number of industries.
* @note The maximum valid IndustryID can be higher than the value returned.
*/
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 IndustryID of a tile, if there is an industry.
* @param tile The tile to find the IndustryID of.
* @return IndustryID of the industry.
* @post Use IsValidIndustry() to see if the industry is valid.
* @note GetIndustryID will return an invalid IndustryID for the
* station tile of industries with a dock/heliport.
*/
static IndustryID GetIndustryID(TileIndex tile);
/**
* 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 char *GetName(IndustryID industry_id);
/**
* See whether an industry currently 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 Whether the industry accepts, temporarily refuses or never accepts this cargo.
*/
static CargoAcceptState 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);
/**
* Get the percentage 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 percentage of given cargo transported from this industry last month.
*/
static int32 GetLastMonthTransportedPercentage(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. All stations that can
* service the industry are counted, your own stations but also your
* opponents stations.
* @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?
* @param industry_id The index of the industry.
* @pre IsValidIndustry(industry_id).
* @return True when the industry has a heliport.
*/
static bool HasHeliport(IndustryID industry_id);
/**
* Gets the location of the industry's heliport.
* @param industry_id The index of the industry.
* @pre IsValidIndustry(industry_id).
* @pre HasHeliport(industry_id).
* @return The location of the industry's heliport.
*/
static TileIndex GetHeliportLocation(IndustryID industry_id);
/**
* Does this industry have a dock?
* @param industry_id The index of the industry.
* @pre IsValidIndustry(industry_id).
* @return True when the industry has a dock.
*/
static bool HasDock(IndustryID industry_id);
/**
* Gets the location of the industry's dock.
* @param industry_id The index of the industry.
* @pre IsValidIndustry(industry_id).
* @pre HasDock(industry_id).
* @return The location of the industry's dock.
*/
static TileIndex GetDockLocation(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 /* SCRIPT_INDUSTRY_HPP */

View File

@@ -0,0 +1,44 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_industrylist.cpp Implementation of AIIndustryList and friends. */
#include "../../stdafx.h"
#include "script_industrylist.hpp"
#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;
FOR_ALL_INDUSTRIES(i) {
for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
if (i->accepts_cargo[j] == cargo_id) this->AddItem(i->index);
}
}
}
AIIndustryList_CargoProducing::AIIndustryList_CargoProducing(CargoID cargo_id)
{
const Industry *i;
FOR_ALL_INDUSTRIES(i) {
for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
if (i->produced_cargo[j] == cargo_id) this->AddItem(i->index);
}
}
}

View File

@@ -0,0 +1,51 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_industrylist.hpp List all the industries. */
#ifndef SCRIPT_INDUSTRYLIST_HPP
#define SCRIPT_INDUSTRYLIST_HPP
#include "script_list.hpp"
/**
* Creates a list of industries that are currently on the map.
* @ingroup AIList
*/
class AIIndustryList : public AIList {
public:
AIIndustryList();
};
/**
* Creates a list of industries that accepts a given cargo.
* @ingroup AIList
*/
class AIIndustryList_CargoAccepting : public AIList {
public:
/**
* @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 AIList {
public:
/**
* @param cargo_id The cargo this industry should produce.
*/
AIIndustryList_CargoProducing(CargoID cargo_id);
};
#endif /* SCRIPT_INDUSTRYLIST_HPP */

View File

@@ -0,0 +1,148 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_industrytype.cpp Implementation of AIIndustryType. */
#include "../../stdafx.h"
#include "script_industrytype.hpp"
#include "script_map.hpp"
#include "script_error.hpp"
#include "../../strings_func.h"
#include "../../industry.h"
#include "../../newgrf_industries.h"
#include "../../core/random_func.hpp"
/* 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 -1;
if (::GetIndustrySpec(industry_type)->IsRawIndustry() && _settings_game.construction.raw_industry_construction == 0) return -1;
return ::GetIndustrySpec(industry_type)->GetConstructionCost();
}
/* static */ 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 (size_t i = 0; i < lengthof(ins->produced_cargo); i++) {
if (ins->produced_cargo[i] != CT_INVALID) list->AddItem(ins->produced_cargo[i]);
}
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 (size_t i = 0; i < lengthof(ins->accepts_cargo); i++) {
if (ins->accepts_cargo[i] != CT_INVALID) list->AddItem(ins->accepts_cargo[i]);
}
return list;
}
/* static */ bool AIIndustryType::CanBuildIndustry(IndustryType industry_type)
{
if (!IsValidIndustryType(industry_type)) return false;
if (::GetIndustryProbabilityCallback(industry_type, IACT_USERCREATION, 1) == 0) 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;
if (::GetIndustryProbabilityCallback(industry_type, IACT_USERCREATION, 1) == 0) 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) << 8) | 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);
}
/* static */ bool AIIndustryType::IsBuiltOnWater(IndustryType industry_type)
{
if (!IsValidIndustryType(industry_type)) return false;
return (::GetIndustrySpec(industry_type)->behaviour & INDUSTRYBEH_BUILT_ONWATER) != 0;
}
/* static */ bool AIIndustryType::HasHeliport(IndustryType industry_type)
{
if (!IsValidIndustryType(industry_type)) return false;
return (::GetIndustrySpec(industry_type)->behaviour & INDUSTRYBEH_AI_AIRSHIP_ROUTES) != 0;
}
/* static */ bool AIIndustryType::HasDock(IndustryType industry_type)
{
if (!IsValidIndustryType(industry_type)) return false;
return (::GetIndustrySpec(industry_type)->behaviour & INDUSTRYBEH_AI_AIRSHIP_ROUTES) != 0;
}

View File

@@ -0,0 +1,153 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_industrytype.hpp Everything to query and build industries. */
#ifndef SCRIPT_INDUSTRYTYPE_HPP
#define SCRIPT_INDUSTRYTYPE_HPP
#include "script_list.hpp"
/**
* Class that handles all industry-type related functions.
*/
class AIIndustryType : public AIObject {
public:
/**
* Special IndustryTypes.
*/
enum SpecialIndustryType {
INDUSTRYTYPE_UNKNOWN = 0xFE, ///< Unknown/unspecific industrytype. (Usable for AIRail::BuildNewGRFRailStation())
INDUSTRYTYPE_TOWN = 0xFF, ///< No industry, but town. (Usable for AIRail::BuildNewGRFRailStation())
};
/**
* 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 char *GetName(IndustryType industry_type);
/**
* Get a list of CargoID possible produced by this industry-type.
* @warning This function only returns the default cargos of the industry type.
* Industries can specify new cargotypes on construction.
* @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.
* @warning This function only returns the default cargos of the industry type.
* Industries can specify new cargotypes on construction.
* @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 build this type of industry at locations of your choice.
* @note Returns false if you can only prospect this type of industry, or not build it at all.
*/
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 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 successfully 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 occurred 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);
/**
* Is this type of industry built on water.
* @param industry_type The type of the industry.
* @pre IsValidIndustryType(industry_type).
* @return True when this type is built on water.
*/
static bool IsBuiltOnWater(IndustryType industry_type);
/**
* Does this type of industry have a heliport?
* @param industry_type The type of the industry.
* @pre IsValidIndustryType(industry_type).
* @return True when this type has a heliport.
*/
static bool HasHeliport(IndustryType industry_type);
/**
* Does this type of industry have a dock?
* @param industry_type The type of the industry.
* @pre IsValidIndustryType(industry_type).
* @return True when this type has a dock.
*/
static bool HasDock(IndustryType industry_type);
};
#endif /* SCRIPT_INDUSTRYTYPE_HPP */

View File

@@ -0,0 +1,21 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_industrytypelist.cpp Implementation of AIIndustryTypeList. */
#include "../../stdafx.h"
#include "script_industrytypelist.hpp"
#include "../../industry.h"
AIIndustryTypeList::AIIndustryTypeList()
{
for (int i = 0; i < NUM_INDUSTRYTYPES; i++) {
if (AIIndustryType::IsValidIndustryType(i)) this->AddItem(i);
}
}

View File

@@ -0,0 +1,27 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_industrytypelist.hpp List all available industry types. */
#ifndef SCRIPT_INDUSTRYTYPELIST_HPP
#define SCRIPT_INDUSTRYTYPELIST_HPP
#include "script_industrytype.hpp"
/**
* Creates a list of valid industry types.
* @ingroup AIList
*/
class AIIndustryTypeList : public AIList {
public:
AIIndustryTypeList();
};
#endif /* SCRIPT_INDUSTRYTYPELIST_HPP */

View File

@@ -0,0 +1,887 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_list.cpp Implementation of AIList. */
#include "../../stdafx.h"
#include "script_list.hpp"
#include "../../debug.h"
#include "../../script/squirrel.hpp"
/**
* Base class for any AIList sorter.
*/
class AIListSorter {
protected:
AIList *list; ///< The list that's being sorted.
bool has_no_more_items; ///< Whether we have more items to iterate over.
int32 item_next; ///< The next item we will show.
public:
/**
* Virtual dtor, needed to mute warnings.
*/
virtual ~AIListSorter() { }
/**
* 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 the sorter has reached the end.
*/
bool IsEnd()
{
return this->list->buckets.empty() || this->has_no_more_items;
}
/**
* Callback from the list if an item gets removed.
*/
virtual void Remove(int item) = 0;
};
/**
* Sort by value, ascending.
*/
class AIListSorterValueAscending : public AIListSorter {
private:
AIList::AIListBucket::iterator bucket_iter; ///< The iterator over the list to find the buckets.
AIList::AIItemList *bucket_list; ///< The current bucket list we're iterator over.
AIList::AIItemList::iterator bucket_list_iter; ///< The iterator over the bucket list.
public:
/**
* Create a new sorter.
* @param list The list to sort.
*/
AIListSorterValueAscending(AIList *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;
}
/**
* Find the next item, and store that information.
*/
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->IsEnd()) return 0;
int32 item_current = this->item_next;
FindNext();
return item_current;
}
void Remove(int item)
{
if (this->IsEnd()) return;
/* If we remove the 'next' item, skip to the next */
if (item == this->item_next) {
FindNext();
return;
}
}
};
/**
* Sort by value, descending.
*/
class AIListSorterValueDescending : public AIListSorter {
private:
AIList::AIListBucket::iterator bucket_iter; ///< The iterator over the list to find the buckets.
AIList::AIItemList *bucket_list; ///< The current bucket list we're iterator over.
AIList::AIItemList::iterator bucket_list_iter; ///< The iterator over the bucket list.
public:
/**
* Create a new sorter.
* @param list The list to sort.
*/
AIListSorterValueDescending(AIList *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;
}
/**
* Find the next item, and store that information.
*/
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->IsEnd()) return 0;
int32 item_current = this->item_next;
FindNext();
return item_current;
}
void Remove(int item)
{
if (this->IsEnd()) return;
/* If we remove the 'next' item, skip to the next */
if (item == this->item_next) {
FindNext();
return;
}
}
};
/**
* Sort by item, ascending.
*/
class AIListSorterItemAscending : public AIListSorter {
private:
AIList::AIListMap::iterator item_iter; ///< The iterator over the items in the map.
public:
/**
* Create a new sorter.
* @param list The list to sort.
*/
AIListSorterItemAscending(AIList *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;
}
/**
* Find the next item, and store that information.
*/
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->IsEnd()) return 0;
int32 item_current = this->item_next;
FindNext();
return item_current;
}
void Remove(int item)
{
if (this->IsEnd()) return;
/* If we remove the 'next' item, skip to the next */
if (item == this->item_next) {
FindNext();
return;
}
}
};
/**
* Sort by item, descending.
*/
class AIListSorterItemDescending : public AIListSorter {
private:
AIList::AIListMap::iterator item_iter; ///< The iterator over the items in the map.
public:
/**
* Create a new sorter.
* @param list The list to sort.
*/
AIListSorterItemDescending(AIList *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;
}
/**
* Find the next item, and store that information.
*/
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->IsEnd()) return 0;
int32 item_current = this->item_next;
FindNext();
return item_current;
}
void Remove(int item)
{
if (this->IsEnd()) return;
/* If we remove the 'next' item, skip to the next */
if (item == this->item_next) {
FindNext();
return;
}
}
};
AIList::AIList()
{
/* Default sorter */
this->sorter = new AIListSorterValueDescending(this);
this->sorter_type = SORT_BY_VALUE;
this->sort_ascending = false;
this->initialized = false;
this->modifications = 0;
}
AIList::~AIList()
{
delete this->sorter;
}
bool AIList::HasItem(int32 item)
{
return this->items.count(item) == 1;
}
void AIList::Clear()
{
this->modifications++;
this->items.clear();
this->buckets.clear();
this->sorter->End();
}
void AIList::AddItem(int32 item, int32 value)
{
this->modifications++;
if (this->HasItem(item)) return;
this->items[item] = 0;
this->buckets[0].insert(item);
this->SetValue(item, value);
}
void AIList::RemoveItem(int32 item)
{
this->modifications++;
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 AIList::Begin()
{
this->initialized = true;
return this->sorter->Begin();
}
int32 AIList::Next()
{
if (this->initialized == false) {
DEBUG(ai, 0, "Next() is invalid as Begin() is never called");
return 0;
}
return this->sorter->Next();
}
bool AIList::IsEmpty()
{
return this->items.empty();
}
bool AIList::IsEnd()
{
if (this->initialized == false) {
DEBUG(ai, 0, "IsEnd() is invalid as Begin() is never called");
return true;
}
return this->sorter->IsEnd();
}
int32 AIList::Count()
{
return (int32)this->items.size();
}
int32 AIList::GetValue(int32 item)
{
if (!this->HasItem(item)) return 0;
return this->items[item];
}
bool AIList::SetValue(int32 item, int32 value)
{
this->modifications++;
if (!this->HasItem(item)) return false;
int32 value_old = this->GetValue(item);
if (value_old == value) return true;
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 AIList::Sort(SorterType sorter, bool ascending)
{
this->modifications++;
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 AIListSorterItemAscending(this);
} else {
this->sorter = new AIListSorterItemDescending(this);
}
break;
case SORT_BY_VALUE:
if (ascending) {
this->sorter = new AIListSorterValueAscending(this);
} else {
this->sorter = new AIListSorterValueDescending(this);
}
break;
default:
this->Sort(SORT_BY_ITEM, false);
return;
}
this->sorter_type = sorter;
this->sort_ascending = ascending;
this->initialized = false;
}
void AIList::AddList(AIList *list)
{
AIListMap *list_items = &list->items;
for (AIListMap::iterator iter = list_items->begin(); iter != list_items->end(); iter++) {
this->AddItem((*iter).first);
this->SetValue((*iter).first, (*iter).second);
}
}
void AIList::RemoveAboveValue(int32 value)
{
this->modifications++;
for (AIListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) {
next_iter = iter; next_iter++;
if ((*iter).second > value) this->RemoveItem((*iter).first);
}
}
void AIList::RemoveBelowValue(int32 value)
{
this->modifications++;
for (AIListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) {
next_iter = iter; next_iter++;
if ((*iter).second < value) this->RemoveItem((*iter).first);
}
}
void AIList::RemoveBetweenValue(int32 start, int32 end)
{
this->modifications++;
for (AIListMap::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->RemoveItem((*iter).first);
}
}
void AIList::RemoveValue(int32 value)
{
this->modifications++;
for (AIListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) {
next_iter = iter; next_iter++;
if ((*iter).second == value) this->RemoveItem((*iter).first);
}
}
void AIList::RemoveTop(int32 count)
{
this->modifications++;
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 (AIListBucket::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 (AIListMap::iterator iter = this->items.begin(); iter != this->items.end(); iter = this->items.begin()) {
if (--count < 0) return;
this->RemoveItem((*iter).first);
}
break;
}
}
void AIList::RemoveBottom(int32 count)
{
this->modifications++;
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 (AIListBucket::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 (AIListMap::reverse_iterator iter = this->items.rbegin(); iter != this->items.rend(); iter = this->items.rbegin()) {
if (--count < 0) return;
this->RemoveItem((*iter).first);
}
break;
}
}
void AIList::RemoveList(AIList *list)
{
this->modifications++;
AIListMap *list_items = &list->items;
for (AIListMap::iterator iter = list_items->begin(); iter != list_items->end(); iter++) {
this->RemoveItem((*iter).first);
}
}
void AIList::KeepAboveValue(int32 value)
{
this->modifications++;
for (AIListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) {
next_iter = iter; next_iter++;
if ((*iter).second <= value) this->RemoveItem((*iter).first);
}
}
void AIList::KeepBelowValue(int32 value)
{
this->modifications++;
for (AIListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) {
next_iter = iter; next_iter++;
if ((*iter).second >= value) this->RemoveItem((*iter).first);
}
}
void AIList::KeepBetweenValue(int32 start, int32 end)
{
this->modifications++;
for (AIListMap::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->RemoveItem((*iter).first);
}
}
void AIList::KeepValue(int32 value)
{
this->modifications++;
for (AIListMap::iterator next_iter, iter = this->items.begin(); iter != this->items.end(); iter = next_iter) {
next_iter = iter; next_iter++;
if ((*iter).second != value) this->RemoveItem((*iter).first);
}
}
void AIList::KeepTop(int32 count)
{
this->modifications++;
this->RemoveBottom(this->Count() - count);
}
void AIList::KeepBottom(int32 count)
{
this->modifications++;
this->RemoveTop(this->Count() - count);
}
void AIList::KeepList(AIList *list)
{
this->modifications++;
AIList tmp;
for (AIListMap::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 AIList::_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 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->SetValue(idx, val);
return 0;
}
SQInteger AIList::_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->IsEnd()) {
sq_pushnull(vm);
return 1;
}
sq_pushinteger(vm, val);
return 1;
}
SQInteger AIList::Valuate(HSQUIRRELVM vm)
{
this->modifications++;
/* The first parameter is the instance of AIList. */
int nparam = sq_gettop(vm) - 1;
if (nparam < 1) {
return sq_throwerror(vm, _SC("You need to give a least a Valuator as parameter to AIList::Valuate"));
}
/* Make sure the valuator function is really a function, and not any
* other type. It's parameter 2 for us, but for the user it's the
* first parameter they give. */
SQObjectType valuator_type = sq_gettype(vm, 2);
if (valuator_type != OT_CLOSURE && valuator_type != OT_NATIVECLOSURE) {
return sq_throwerror(vm, _SC("parameter 1 has an invalid type (expected function)"));
}
/* Don't allow docommand from a Valuator, as we can't resume in
* mid C++-code. */
bool backup_allow = AIObject::GetAllowDoCommand();
AIObject::SetAllowDoCommand(false);
/* Push the function to call */
sq_push(vm, 2);
for (AIListMap::iterator iter = this->items.begin(); iter != this->items.end(); iter++) {
/* Check for changing of items. */
int previous_modification_count = this->modifications;
/* Push the root table as instance object, this is what squirrel does for meta-functions. */
sq_pushroottable(vm);
/* Push all arguments for the valuator function. */
sq_pushinteger(vm, (*iter).first);
for (int i = 0; i < nparam - 1; i++) {
sq_push(vm, i + 3);
}
/* Call the function. Squirrel pops all parameters and pushes the return value. */
if (SQ_FAILED(sq_call(vm, nparam + 1, SQTrue, SQTrue))) {
AIObject::SetAllowDoCommand(backup_allow);
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: {
/* See below for explanation. The extra pop is the return value. */
sq_pop(vm, nparam + 4);
AIObject::SetAllowDoCommand(backup_allow);
return sq_throwerror(vm, _SC("return value of valuator is not valid (not integer/bool)"));
}
}
/* Was something changed? */
if (previous_modification_count != this->modifications) {
/* See below for explanation. The extra pop is the return value. */
sq_pop(vm, nparam + 4);
AIObject::SetAllowDoCommand(backup_allow);
return sq_throwerror(vm, _SC("modifying valuated list outside of valuator function"));
}
this->SetValue((*iter).first, value);
/* Pop the return value. */
sq_poptop(vm);
Squirrel::DecreaseOps(vm, 5);
}
/* Pop from the squirrel stack:
* 1. The root stable (as instance object).
* 2. The valuator function.
* 3. The parameters given to this function.
* 4. The AIList instance object. */
sq_pop(vm, nparam + 3);
AIObject::SetAllowDoCommand(backup_allow);
return 0;
}

View File

@@ -0,0 +1,283 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_list.hpp A list which can keep item/value pairs, which you can walk. */
/** @defgroup AIList Classes that create a list of items. */
#ifndef SCRIPT_LIST_HPP
#define SCRIPT_LIST_HPP
#include "script_object.hpp"
#include <map>
#include <set>
class AIListSorter;
/**
* Class that creates a list which can keep item/value pairs, which you can walk.
*/
class AIList : public AIObject {
public:
/** 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.
};
/** Sort ascending */
static const bool SORT_ASCENDING = true;
/** Sort descnding */
static const bool SORT_DESCENDING = false;
private:
AIListSorter *sorter; ///< Sorting algorithm
SorterType sorter_type; ///< Sorting type
bool sort_ascending; ///< Whether to sort ascending or descending
bool initialized; ///< Whether an iteration has been started
int modifications; ///< Number of modification that has been done. To prevent changing data while valuating.
public:
typedef std::set<int32> AIItemList; ///< The list of items inside the bucket
typedef std::map<int32, AIItemList> AIListBucket; ///< The bucket list per value
typedef std::map<int32, int32> AIListMap; ///< List per item
AIListMap items; ///< The items in the list
AIListBucket buckets; ///< The items in the list, sorted by value
AIList();
~AIList();
/**
* Add a single item to the list.
* @param item the item to add. Should be unique, otherwise it is ignored.
* @param value the value to assign.
* @note the value is set to 0 by default.
*/
void AddItem(int32 item, int32 value = 0);
/**
* Remove a single item from the list.
* @param item the item to remove. If not existing, it is ignored.
*/
void RemoveItem(int32 item);
/**
* 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.
* @note returns 0 if beyond end-of-list. Use IsEnd() to check for end-of-list.
*/
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 IsEnd() 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 element left. In other words, if this is false,
* the last call to Begin() or Next() returned a valid item.
* @return true if the current item is beyond end-of-list.
*/
bool IsEnd();
/**
* 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.
* @see SORT_ASCENDING SORT_DESCENDING
*/
void Sort(SorterType sorter, bool ascending);
/**
* Add one list to another 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(AIList *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(AIList *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(AIList *list);
#ifndef DOXYGEN_AI_DOCS
/**
* Used for 'foreach()' and [] get from Squirrel.
*/
SQInteger _get(HSQUIRRELVM vm);
/**
* Used for [] set from Squirrel.
*/
SQInteger _set(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 may not add, remove or change (setting the value of) items while
* valuating. You may also not (re)sort while 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_AI_DOCS */
};
#endif /* SCRIPT_LIST_HPP */

View File

@@ -0,0 +1,92 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_log.cpp Implementation of AILog. */
#include "../../stdafx.h"
#include "script_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 *>(400);
log->type = CallocT<AILog::AILogType>(400);
log->count = 400;
log->pos = log->count - 1;
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;
}

View File

@@ -0,0 +1,85 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_log.hpp Everything to handle and issue log messages. */
#ifndef SCRIPT_LOG_HPP
#define SCRIPT_LOG_HPP
#include "script_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:
#ifndef EXPORT_SKIP
/**
* 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.
};
#endif /* EXPORT_SKIP */
/**
* 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);
#ifndef EXPORT_SKIP
/**
* Free the log pointer.
* @note DO NOT CALL YOURSELF; leave it to the internal AI programming.
*/
static void FreeLogPointer();
#endif /* EXPORT_SKIP */
private:
/**
* Internal command to log the message in a common way.
*/
static void Log(AILog::AILogType level, const char *message);
};
#endif /* SCRIPT_LOG_HPP */

View File

@@ -0,0 +1,75 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_map.cpp Implementation of AIMap. */
#include "../../stdafx.h"
#include "script_map.hpp"
#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 */ int32 AIMap::GetTileX(TileIndex t)
{
if (!::IsValidTile(t)) return -1;
return ::TileX(t);
}
/* static */ int32 AIMap::GetTileY(TileIndex t)
{
if (!::IsValidTile(t)) return -1;
return ::TileY(t);
}
/* static */ TileIndex AIMap::GetTileIndex(uint32 x, uint32 y)
{
return ::TileXY(x, y);
}
/* static */ int32 AIMap::DistanceManhattan(TileIndex t1, TileIndex t2)
{
if (!::IsValidTile(t1) || !::IsValidTile(t2)) return -1;
return ::DistanceManhattan(t1, t2);
}
/* static */ int32 AIMap::DistanceMax(TileIndex t1, TileIndex t2)
{
if (!::IsValidTile(t1) || !::IsValidTile(t2)) return -1;
return ::DistanceMax(t1, t2);
}
/* static */ int32 AIMap::DistanceSquare(TileIndex t1, TileIndex t2)
{
if (!::IsValidTile(t1) || !::IsValidTile(t2)) return -1;
return ::DistanceSquare(t1, t2);
}
/* static */ int32 AIMap::DistanceFromEdge(TileIndex t)
{
if (!::IsValidTile(t)) return -1;
return ::DistanceFromEdge(t);
}

View File

@@ -0,0 +1,125 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_map.hpp Everything to query and manipulate map metadata. */
#ifndef SCRIPT_MAP_HPP
#define SCRIPT_MAP_HPP
#include "script_object.hpp"
#include "../../tile_type.h"
/**
* Class that handles all map related functions.
*/
class AIMap : public AIObject {
public:
static const int TILE_INVALID = (int)INVALID_TILE; ///< Invalid TileIndex.
/**
* 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 int32 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 int32 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 int32 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 int32 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 int32 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 int32 DistanceFromEdge(TileIndex tile);
};
#endif /* SCRIPT_MAP_HPP */

View File

@@ -0,0 +1,162 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_marine.cpp Implementation of AIMarine. */
#include "../../stdafx.h"
#include "script_marine.hpp"
#include "script_station.hpp"
#include "../../station_base.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;
DiagDirection to_other_tile = ::DiagdirBetweenTiles(t2, t1);
/* Determine the reachable tracks from the shared edge */
TrackBits gtts1 = ::TrackStatusToTrackBits(::GetTileTrackStatus(t1, TRANSPORT_WATER, 0, to_other_tile)) & ::DiagdirReachesTracks(to_other_tile);
if (gtts1 == TRACK_BIT_NONE) return false;
to_other_tile = ReverseDiagDir(to_other_tile);
TrackBits gtts2 = ::TrackStatusToTrackBits(::GetTileTrackStatus(t2, TRANSPORT_WATER, 0, to_other_tile)) & ::DiagdirReachesTracks(to_other_tile);
return gtts2 != TRACK_BIT_NONE;
}
/* static */ bool AIMarine::BuildWaterDepot(TileIndex tile, TileIndex front)
{
EnforcePrecondition(false, ::IsValidTile(tile));
EnforcePrecondition(false, ::IsValidTile(front));
EnforcePrecondition(false, (::TileX(front) == ::TileX(tile)) != (::TileY(front) == ::TileY(tile)));
return AIObject::DoCommand(tile, ::TileX(front) == ::TileX(tile), 0, CMD_BUILD_SHIP_DEPOT);
}
/* static */ bool AIMarine::BuildDock(TileIndex tile, StationID station_id)
{
EnforcePrecondition(false, ::IsValidTile(tile));
EnforcePrecondition(false, station_id == AIStation::STATION_NEW || station_id == AIStation::STATION_JOIN_ADJACENT || AIStation::IsValidStation(station_id));
uint p1 = station_id == AIStation::STATION_JOIN_ADJACENT ? 0 : 1;
uint p2 = (AIStation::IsValidStation(station_id) ? station_id : INVALID_STATION) << 16;
return AIObject::DoCommand(tile, p1, p2, 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, WATER_CLASS_CANAL, 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);
}
/* static */ Money AIMarine::GetBuildCost(BuildType build_type)
{
switch (build_type) {
case BT_DOCK: return ::GetPrice(PR_BUILD_STATION_DOCK, 1, NULL);
case BT_DEPOT: return ::GetPrice(PR_BUILD_DEPOT_SHIP, 1, NULL);
case BT_BUOY: return ::GetPrice(PR_BUILD_WAYPOINT_BUOY, 1, NULL);
default: return -1;
}
}

View File

@@ -0,0 +1,210 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_marine.hpp Everything to query and build marine. */
#ifndef SCRIPT_MARINE_HPP
#define SCRIPT_MARINE_HPP
#include "script_error.hpp"
/**
* Class that handles all marine related functions.
*/
class AIMarine : public AIObject {
public:
/**
* 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_ERROR_MUST_BE_BUILT_ON_WATER]
};
/**
* Types of water-related objects in the game.
*/
enum BuildType {
BT_DOCK, ///< Build a dock
BT_DEPOT, ///< Build a ship depot
BT_BUOY, ///< Build a buoy
};
/**
* 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 front A tile on the same axis with 'tile' as the depot shall be oriented.
* @pre AIMap::IsValidTile(tile).
* @pre AIMap::IsValidTile(front).
* @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.
* @note The depot will be built towards the south from 'tile', not necessarily towards 'front'.
*/
static bool BuildWaterDepot(TileIndex tile, TileIndex front);
/**
* Builds a dock where tile is the tile still on land.
* @param tile The tile still on land of the dock.
* @param station_id The station to join, AIStation::STATION_NEW or AIStation::STATION_JOIN_ADJACENT.
* @pre AIMap::IsValidTile(tile).
* @pre station_id == AIStation::STATION_NEW || station_id == AIStation::STATION_JOIN_ADJACENT || AIStation::IsValidStation(station_id).
* @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, StationID station_id);
/**
* 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);
/**
* Get the baseprice of building a water-related object.
* @param build_type the type of object to build
* @return The baseprice of building the given object.
*/
static Money GetBuildCost(BuildType build_type);
};
#endif /* SCRIPT_MARINE_HPP */

View File

@@ -0,0 +1,279 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_object.cpp Implementation of AIObject. */
#include "../../stdafx.h"
#include "../../script/squirrel.hpp"
#include "../../command_func.h"
#include "../../network/network.h"
#include "../../tunnelbridge.h"
#include "../../ai/ai_storage.hpp"
#include "../../ai/ai_instance.hpp"
#include "script_error.hpp"
/**
* Get the storage associated with the current AIInstance.
* @return The storage.
*/
static AIStorage *GetStorage()
{
return AIObject::GetActiveInstance()->GetStorage();
}
/* static */ AIInstance *AIObject::ActiveInstance::active = NULL;
AIObject::ActiveInstance::ActiveInstance(AIInstance *instance)
{
this->last_active = AIObject::ActiveInstance::active;
AIObject::ActiveInstance::active = instance;
}
AIObject::ActiveInstance::~ActiveInstance()
{
AIObject::ActiveInstance::active = this->last_active;
}
/* static */ AIInstance *AIObject::GetActiveInstance()
{
assert(AIObject::ActiveInstance::active != NULL);
return AIObject::ActiveInstance::active;
}
/* static */ void AIObject::SetDoCommandDelay(uint ticks)
{
assert(ticks > 0);
GetStorage()->delay = ticks;
}
/* static */ uint AIObject::GetDoCommandDelay()
{
return GetStorage()->delay;
}
/* static */ void AIObject::SetDoCommandMode(AIModeProc *proc, AIObject *instance)
{
GetStorage()->mode = proc;
GetStorage()->mode_instance = instance;
}
/* static */ AIModeProc *AIObject::GetDoCommandMode()
{
return GetStorage()->mode;
}
/* static */ AIObject *AIObject::GetDoCommandModeInstance()
{
return GetStorage()->mode_instance;
}
/* static */ void AIObject::SetDoCommandCosts(Money value)
{
GetStorage()->costs = CommandCost(value);
}
/* static */ void AIObject::IncreaseDoCommandCosts(Money value)
{
GetStorage()->costs.AddCost(value);
}
/* static */ Money AIObject::GetDoCommandCosts()
{
return GetStorage()->costs.GetCost();
}
/* static */ void AIObject::SetLastError(AIErrorType last_error)
{
GetStorage()->last_error = last_error;
}
/* static */ AIErrorType AIObject::GetLastError()
{
return GetStorage()->last_error;
}
/* static */ void AIObject::SetLastCost(Money last_cost)
{
GetStorage()->last_cost = last_cost;
}
/* static */ Money AIObject::GetLastCost()
{
return GetStorage()->last_cost;
}
/* static */ void AIObject::SetRoadType(RoadType road_type)
{
GetStorage()->road_type = road_type;
}
/* static */ RoadType AIObject::GetRoadType()
{
return GetStorage()->road_type;
}
/* static */ void AIObject::SetRailType(RailType rail_type)
{
GetStorage()->rail_type = rail_type;
}
/* static */ RailType AIObject::GetRailType()
{
return GetStorage()->rail_type;
}
/* static */ 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);
}
/* static */ bool AIObject::GetLastCommandRes()
{
return GetStorage()->last_command_res;
}
/* static */ void AIObject::SetNewVehicleID(VehicleID vehicle_id)
{
GetStorage()->new_vehicle_id = vehicle_id;
}
/* static */ VehicleID AIObject::GetNewVehicleID()
{
return GetStorage()->new_vehicle_id;
}
/* static */ void AIObject::SetNewSignID(SignID sign_id)
{
GetStorage()->new_sign_id = sign_id;
}
/* static */ SignID AIObject::GetNewSignID()
{
return GetStorage()->new_sign_id;
}
/* static */ void AIObject::SetNewTunnelEndtile(TileIndex tile)
{
GetStorage()->new_tunnel_endtile = tile;
}
/* static */ TileIndex AIObject::GetNewTunnelEndtile()
{
return GetStorage()->new_tunnel_endtile;
}
/* static */ void AIObject::SetNewGroupID(GroupID group_id)
{
GetStorage()->new_group_id = group_id;
}
/* static */ GroupID AIObject::GetNewGroupID()
{
return GetStorage()->new_group_id;
}
/* static */ void AIObject::SetAllowDoCommand(bool allow)
{
GetStorage()->allow_do_command = allow;
}
/* static */ bool AIObject::GetAllowDoCommand()
{
return GetStorage()->allow_do_command;
}
/* static */ bool AIObject::CanSuspend()
{
Squirrel *squirrel = AIObject::GetActiveInstance()->engine;
return GetStorage()->allow_do_command && squirrel->CanSuspend();
}
/* static */ void *&AIObject::GetEventPointer()
{
return GetStorage()->event_data;
}
/* static */ void *&AIObject::GetLogPointer()
{
return GetStorage()->log_data;
}
/* static */ 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;
}
/* static */ int AIObject::GetCallbackVariable(int index)
{
return GetStorage()->callback_value[index];
}
/* static */ bool AIObject::DoCommand(TileIndex tile, uint32 p1, uint32 p2, uint cmd, const char *text, AISuspendCallbackProc *callback)
{
if (!AIObject::CanSuspend()) {
throw AI_FatalError("You are not allowed to execute any DoCommand (even indirect) in your constructor, Save(), Load(), and any valuator.");
}
/* Set the default callback to return a true/false result of the DoCommand */
if (callback == NULL) callback = &AIInstance::DoCommandReturn;
/* Are we only interested in the estimate costs? */
bool estimate_only = GetDoCommandMode() != NULL && !GetDoCommandMode()();
#ifdef ENABLE_NETWORK
/* Only set p2 when the command does not come from the network. */
if (GetCommandFlags(cmd) & CMD_CLIENT_ID && p2 == 0) p2 = UINT32_MAX;
#endif
/* Try to perform the command. */
CommandCost res = ::DoCommandPInternal(tile, p1, p2, cmd, _networking ? CcAI : NULL, text, false, estimate_only);
/* We failed; set the error and bail out */
if (res.Failed()) {
SetLastError(AIError::StringToError(res.GetErrorMessage()));
return false;
}
/* No error, then clear it. */
SetLastError(AIError::ERR_NONE);
/* Estimates, update the cost for the estimate and be done */
if (estimate_only) {
IncreaseDoCommandCosts(res.GetCost());
return true;
}
/* Costs of this operation. */
SetLastCost(res.GetCost());
SetLastCommandRes(true);
if (_networking) {
/* Suspend the AI till the command is really executed. */
throw AI_VMSuspend(-(int)GetDoCommandDelay(), callback);
} else {
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();
}

View File

@@ -0,0 +1,249 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_object.hpp Main object, on which all objects depend. */
#ifndef SCRIPT_OBJECT_HPP
#define SCRIPT_OBJECT_HPP
#include "../../misc/countedptr.hpp"
#include "../../road_type.h"
#include "../../rail_type.h"
#include "script_types.hpp"
/**
* The callback function when an AI suspends.
*/
typedef void (AISuspendCallbackProc)(class AIInstance *instance);
/**
* The callback function for Mode-classes.
*/
typedef bool (AIModeProc)();
/**
* 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 class AIInstance;
#ifndef DOXYGEN_AI_DOCS
protected:
/**
* A class that handles the current active instance. By instantiating it at
* the beginning of a function with the current active instance, it remains
* active till the scope of the variable closes. It then automatically
* reverts to the active instance it was before instantiating.
*/
class ActiveInstance {
friend class AIObject;
public:
ActiveInstance(AIInstance *instance);
~ActiveInstance();
private:
AIInstance *last_active; ///< The active instance before we go instantiated.
static AIInstance *active; ///< The global current active instance.
};
public:
/**
* Store the latest result of a DoCommand per company.
* @param res The result of the last command.
*/
static void SetLastCommandRes(bool res);
/**
* Get the currently active instance.
* @return The instance.
*/
static class AIInstance *GetActiveInstance();
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();
/**
* Store a allow_do_command per company.
* @param allow The new allow.
*/
static void SetAllowDoCommand(bool allow);
/**
* Get the internal value of allow_do_command. This can differ
* from CanSuspend() if the reason we are not allowed
* to execute a DoCommand is in squirrel and not the API.
* In that case use this function to restore the previous value.
* @return True iff DoCommands are allowed in the current scope.
*/
static bool GetAllowDoCommand();
/**
* Set the cost of the last command.
*/
static void SetLastCost(Money last_cost);
/**
* Get the cost of the last command.
*/
static Money GetLastCost();
/**
* Set a variable that can be used by callback functions to pass information.
*/
static void SetCallbackVariable(int index, int value);
/**
* Get the variable that is used by callback functions to pass information.
*/
static int GetCallbackVariable(int index);
/**
* Can we suspend the AI at this moment?
*/
static bool CanSuspend();
/**
* Get the pointer to store event data in.
*/
static void *&GetEventPointer();
/**
* Get the pointer to store log message in.
*/
static void *&GetLogPointer();
private:
/**
* Store a new_vehicle_id per company.
* @param vehicle_id The new VehicleID.
*/
static void SetNewVehicleID(VehicleID vehicle_id);
/**
* Store a new_sign_id per company.
* @param sign_id The new SignID.
*/
static void SetNewSignID(SignID sign_id);
/**
* Store a new_tunnel_endtile per company.
* @param tile The new TileIndex.
*/
static void SetNewTunnelEndtile(TileIndex tile);
/**
* Store a new_group_id per company.
* @param group_id The new GroupID.
*/
static void SetNewGroupID(GroupID group_id);
#endif /* DOXYGEN_AI_DOCS */
};
#endif /* SCRIPT_OBJECT_HPP */

View File

@@ -0,0 +1,657 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_order.cpp Implementation of AIOrder. */
#include "../../stdafx.h"
#include "script_order.hpp"
#include "script_vehicle.hpp"
#include "script_cargo.hpp"
#include "../../ai/ai_instance.hpp"
#include "../../debug.h"
#include "../../vehicle_base.h"
#include "../../roadstop_base.h"
#include "../../depot_base.h"
#include "../../station_base.h"
#include "../../waypoint_base.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:
if (IsBuoy(t) || IsRailWaypoint(t)) return OT_GOTO_WAYPOINT;
if (IsHangar(t)) return OT_GOTO_DEPOT;
return OT_GOTO_STATION;
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:
if (IsRailDepot(t)) return OT_GOTO_DEPOT;
break;
}
return OT_END;
}
/* static */ bool AIOrder::IsValidVehicleOrder(VehicleID vehicle_id, OrderPosition order_position)
{
return AIVehicle::IsValidVehicle(vehicle_id) && order_position >= 0 && (order_position < ::Vehicle::Get(vehicle_id)->GetNumManualOrders() || order_position == ORDER_CURRENT);
}
/**
* Get the current order the vehicle is executing. If the current order is in
* the order list, return the order from the orderlist. If the current order
* was a manual order, return the current order.
*/
static const Order *ResolveOrder(VehicleID vehicle_id, AIOrder::OrderPosition order_position)
{
const Vehicle *v = ::Vehicle::Get(vehicle_id);
if (order_position == AIOrder::ORDER_CURRENT) {
const Order *order = &v->current_order;
if (order->GetType() == OT_GOTO_DEPOT && !(order->GetDepotOrderType() & ODTFB_PART_OF_ORDERS)) return order;
order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position);
if (order_position == AIOrder::ORDER_INVALID) return NULL;
}
const Order *order = v->orders.list->GetFirstOrder();
while (order->GetType() == OT_IMPLICIT) order = order->next;
while (order_position > 0) {
order_position = (AIOrder::OrderPosition)(order_position - 1);
order = order->next;
while (order->GetType() == OT_IMPLICIT) order = order->next;
}
return order;
}
/**
* Convert an AIOrder::OrderPosition (which is the manual order index) to an order index
* as expected by the OpenTTD commands.
* @param order_position The OrderPosition to convert.
* @return An OpenTTD-internal index for the same order.
*/
static int AIOrderPositionToRealOrderPosition(VehicleID vehicle_id, AIOrder::OrderPosition order_position)
{
const Vehicle *v = ::Vehicle::Get(vehicle_id);
if (order_position == v->GetNumManualOrders()) return v->GetNumOrders();
assert(AIOrder::IsValidVehicleOrder(vehicle_id, order_position));
int res = (int)order_position;
const Order *order = v->orders.list->GetFirstOrder();
for (; order->GetType() == OT_IMPLICIT; order = order->next) res++;
while (order_position > 0) {
order_position = (AIOrder::OrderPosition)(order_position - 1);
order = order->next;
for (; order->GetType() == OT_IMPLICIT; order = order->next) res++;
}
return res;
}
/* static */ bool AIOrder::IsGotoStationOrder(VehicleID vehicle_id, OrderPosition order_position)
{
if (!IsValidVehicleOrder(vehicle_id, order_position)) return false;
const Order *order = ::ResolveOrder(vehicle_id, order_position);
return order != NULL && order->GetType() == OT_GOTO_STATION;
}
/* static */ bool AIOrder::IsGotoDepotOrder(VehicleID vehicle_id, OrderPosition order_position)
{
if (!IsValidVehicleOrder(vehicle_id, order_position)) return false;
const Order *order = ::ResolveOrder(vehicle_id, order_position);
return order != NULL && order->GetType() == OT_GOTO_DEPOT;
}
/* static */ bool AIOrder::IsGotoWaypointOrder(VehicleID vehicle_id, OrderPosition order_position)
{
if (!IsValidVehicleOrder(vehicle_id, order_position)) return false;
const Order *order = ::ResolveOrder(vehicle_id, order_position);
return order != NULL && order->GetType() == OT_GOTO_WAYPOINT;
}
/* static */ bool AIOrder::IsConditionalOrder(VehicleID vehicle_id, OrderPosition order_position)
{
if (order_position == ORDER_CURRENT) return false;
if (!IsValidVehicleOrder(vehicle_id, order_position)) return false;
const Order *order = ::Vehicle::Get(vehicle_id)->GetOrder(AIOrderPositionToRealOrderPosition(vehicle_id, order_position));
return order->GetType() == OT_CONDITIONAL;
}
/* static */ bool AIOrder::IsVoidOrder(VehicleID vehicle_id, OrderPosition order_position)
{
if (order_position == ORDER_CURRENT) return false;
if (!IsValidVehicleOrder(vehicle_id, order_position)) return false;
const Order *order = ::ResolveOrder(vehicle_id, order_position);
return order->GetType() == OT_DUMMY;
}
/* static */ bool AIOrder::IsRefitOrder(VehicleID vehicle_id, OrderPosition order_position)
{
if (!IsValidVehicleOrder(vehicle_id, order_position)) return false;
const Order *order = ::ResolveOrder(vehicle_id, order_position);
return order != NULL && order->IsRefit();
}
/* static */ bool AIOrder::IsCurrentOrderPartOfOrderList(VehicleID vehicle_id)
{
if (!AIVehicle::IsValidVehicle(vehicle_id)) return false;
if (GetOrderCount(vehicle_id) == 0) return false;
const Order *order = &::Vehicle::Get(vehicle_id)->current_order;
if (order->GetType() != OT_GOTO_DEPOT) return true;
return (order->GetDepotOrderType() & ODTFB_PART_OF_ORDERS) != 0;
}
/* static */ AIOrder::OrderPosition AIOrder::ResolveOrderPosition(VehicleID vehicle_id, OrderPosition order_position)
{
if (!AIVehicle::IsValidVehicle(vehicle_id)) return ORDER_INVALID;
if (order_position == ORDER_CURRENT) {
int cur_order_pos = ::Vehicle::Get(vehicle_id)->cur_real_order_index;
const Order *order = ::Vehicle::Get(vehicle_id)->GetOrder(0);
if (order == NULL) return ORDER_INVALID;
int num_implicit_orders = 0;
for (int i = 0; i < cur_order_pos; i++) {
if (order->GetType() == OT_IMPLICIT) num_implicit_orders++;
order = order->next;
}
return (AIOrder::OrderPosition)(cur_order_pos - num_implicit_orders);
}
return (order_position >= 0 && order_position < ::Vehicle::Get(vehicle_id)->GetNumManualOrders()) ? order_position : ORDER_INVALID;
}
/* static */ bool AIOrder::AreOrderFlagsValid(TileIndex destination, AIOrderFlags order_flags)
{
OrderType ot = (order_flags & AIOF_GOTO_NEAREST_DEPOT) ? OT_GOTO_DEPOT : ::GetOrderTypeByTile(destination);
switch (ot) {
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_DEPOT_FLAGS)) == 0 &&
((order_flags & AIOF_SERVICE_IF_NEEDED) == 0 || (order_flags & AIOF_STOP_IN_DEPOT) == 0);
case OT_GOTO_WAYPOINT: return (order_flags & ~(AIOF_NON_STOP_FLAGS)) == 0;
default: return false;
}
}
/* static */ bool AIOrder::IsValidConditionalOrder(OrderCondition condition, CompareFunction compare)
{
switch (condition) {
case OC_LOAD_PERCENTAGE:
case OC_RELIABILITY:
case OC_MAX_SPEED:
case OC_AGE:
case OC_REMAINING_LIFETIME:
return compare >= CF_EQUALS && compare <= CF_MORE_EQUALS;
case OC_REQUIRES_SERVICE:
return compare == CF_IS_TRUE || compare == CF_IS_FALSE;
case OC_UNCONDITIONALLY:
return true;
default: return false;
}
}
/* static */ int32 AIOrder::GetOrderCount(VehicleID vehicle_id)
{
return AIVehicle::IsValidVehicle(vehicle_id) ? ::Vehicle::Get(vehicle_id)->GetNumManualOrders() : -1;
}
/* static */ TileIndex AIOrder::GetOrderDestination(VehicleID vehicle_id, OrderPosition order_position)
{
if (!IsValidVehicleOrder(vehicle_id, order_position)) return INVALID_TILE;
const Order *order = ::ResolveOrder(vehicle_id, order_position);
if (order == NULL || order->GetType() == OT_CONDITIONAL) return INVALID_TILE;
const Vehicle *v = ::Vehicle::Get(vehicle_id);
switch (order->GetType()) {
case OT_GOTO_DEPOT: {
/* We don't know where the nearest depot is... (yet) */
if (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) return INVALID_TILE;
if (v->type != VEH_AIRCRAFT) return ::Depot::Get(order->GetDestination())->xy;
/* Aircraft's hangars are referenced by StationID, not DepotID */
const Station *st = ::Station::Get(order->GetDestination());
if (!st->airport.HasHangar()) return INVALID_TILE;
return st->airport.GetHangarTile(0);
}
case OT_GOTO_STATION: {
const Station *st = ::Station::Get(order->GetDestination());
if (st->train_station.tile != INVALID_TILE) {
TILE_AREA_LOOP(t, st->train_station) {
if (st->TileBelongsToRailStation(t)) return t;
}
} else if (st->dock_tile != INVALID_TILE) {
return st->dock_tile;
} else if (st->bus_stops != NULL) {
return st->bus_stops->xy;
} else if (st->truck_stops != NULL) {
return st->truck_stops->xy;
} else if (st->airport.tile != INVALID_TILE) {
TILE_AREA_LOOP(tile, st->airport) {
if (st->TileBelongsToAirport(tile) && !::IsHangar(tile)) return tile;
}
}
return INVALID_TILE;
}
case OT_GOTO_WAYPOINT: {
const Waypoint *wp = ::Waypoint::Get(order->GetDestination());
if (wp->train_station.tile != INVALID_TILE) {
TILE_AREA_LOOP(t, wp->train_station) {
if (wp->TileBelongsToRailStation(t)) return t;
}
}
/* If the waypoint has no rail waypoint tiles, it must have a buoy */
return wp->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 = ::ResolveOrder(vehicle_id, order_position);
if (order == NULL || order->GetType() == OT_CONDITIONAL || order->GetType() == OT_DUMMY) return AIOF_INVALID;
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;
if (order->GetDepotActionType() & ODATFB_HALT) order_flags |= AIOF_STOP_IN_DEPOT;
if (order->GetDepotActionType() & ODATFB_NEAREST_DEPOT) order_flags |= AIOF_GOTO_NEAREST_DEPOT;
break;
case OT_GOTO_STATION:
order_flags |= (AIOrderFlags)(order->GetLoadType() << 5);
order_flags |= (AIOrderFlags)(order->GetUnloadType() << 2);
break;
default: break;
}
return order_flags;
}
/* static */ AIOrder::OrderPosition AIOrder::GetOrderJumpTo(VehicleID vehicle_id, OrderPosition order_position)
{
if (!IsValidVehicleOrder(vehicle_id, order_position)) return ORDER_INVALID;
if (order_position == ORDER_CURRENT || !IsConditionalOrder(vehicle_id, order_position)) return ORDER_INVALID;
const Order *order = ::ResolveOrder(vehicle_id, order_position);
return (OrderPosition)order->GetConditionSkipToOrder();
}
/* static */ AIOrder::OrderCondition AIOrder::GetOrderCondition(VehicleID vehicle_id, OrderPosition order_position)
{
if (!IsValidVehicleOrder(vehicle_id, order_position)) return OC_INVALID;
if (order_position == ORDER_CURRENT || !IsConditionalOrder(vehicle_id, order_position)) return OC_INVALID;
const Order *order = ::ResolveOrder(vehicle_id, order_position);
return (OrderCondition)order->GetConditionVariable();
}
/* static */ AIOrder::CompareFunction AIOrder::GetOrderCompareFunction(VehicleID vehicle_id, OrderPosition order_position)
{
if (!IsValidVehicleOrder(vehicle_id, order_position)) return CF_INVALID;
if (order_position == ORDER_CURRENT || !IsConditionalOrder(vehicle_id, order_position)) return CF_INVALID;
const Order *order = ::ResolveOrder(vehicle_id, order_position);
return (CompareFunction)order->GetConditionComparator();
}
/* static */ int32 AIOrder::GetOrderCompareValue(VehicleID vehicle_id, OrderPosition order_position)
{
if (!IsValidVehicleOrder(vehicle_id, order_position)) return -1;
if (order_position == ORDER_CURRENT || !IsConditionalOrder(vehicle_id, order_position)) return -1;
const Order *order = ::ResolveOrder(vehicle_id, order_position);
int32 value = order->GetConditionValue();
if (order->GetConditionVariable() == OCV_MAX_SPEED) value = value * 16 / 10;
return value;
}
/* static */ AIOrder::StopLocation AIOrder::GetStopLocation(VehicleID vehicle_id, OrderPosition order_position)
{
if (!IsValidVehicleOrder(vehicle_id, order_position)) return STOPLOCATION_INVALID;
if (AIVehicle::GetVehicleType(vehicle_id) != AIVehicle::VT_RAIL) return STOPLOCATION_INVALID;
if (!IsGotoStationOrder(vehicle_id, order_position)) return STOPLOCATION_INVALID;
const Order *order = ::ResolveOrder(vehicle_id, order_position);
return (AIOrder::StopLocation)order->GetStopLocation();
}
/* static */ CargoID AIOrder::GetOrderRefit(VehicleID vehicle_id, OrderPosition order_position)
{
if (!IsValidVehicleOrder(vehicle_id, order_position)) return CT_NO_REFIT;
if (order_position != ORDER_CURRENT && !IsGotoStationOrder(vehicle_id, order_position) && !IsGotoDepotOrder(vehicle_id, order_position)) return CT_NO_REFIT;
const Order *order = ::ResolveOrder(vehicle_id, order_position);
return order->IsRefit() ? order->GetRefitCargo() : (CargoID)CT_NO_REFIT;
}
/* static */ bool AIOrder::SetOrderJumpTo(VehicleID vehicle_id, OrderPosition order_position, OrderPosition jump_to)
{
EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position));
EnforcePrecondition(false, order_position != ORDER_CURRENT && IsConditionalOrder(vehicle_id, order_position));
EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, jump_to) && jump_to != ORDER_CURRENT);
return AIObject::DoCommand(0, vehicle_id | (order_position << 20), MOF_COND_DESTINATION | (jump_to << 4), CMD_MODIFY_ORDER);
}
/* static */ bool AIOrder::SetOrderCondition(VehicleID vehicle_id, OrderPosition order_position, OrderCondition condition)
{
EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position));
EnforcePrecondition(false, order_position != ORDER_CURRENT && IsConditionalOrder(vehicle_id, order_position));
EnforcePrecondition(false, condition >= OC_LOAD_PERCENTAGE && condition <= OC_REMAINING_LIFETIME);
int order_pos = AIOrderPositionToRealOrderPosition(vehicle_id, order_position);
return AIObject::DoCommand(0, vehicle_id | (order_pos << 20), MOF_COND_VARIABLE | (condition << 4), CMD_MODIFY_ORDER);
}
/* static */ bool AIOrder::SetOrderCompareFunction(VehicleID vehicle_id, OrderPosition order_position, CompareFunction compare)
{
EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position));
EnforcePrecondition(false, order_position != ORDER_CURRENT && IsConditionalOrder(vehicle_id, order_position));
EnforcePrecondition(false, compare >= CF_EQUALS && compare <= CF_IS_FALSE);
int order_pos = AIOrderPositionToRealOrderPosition(vehicle_id, order_position);
return AIObject::DoCommand(0, vehicle_id | (order_pos << 20), MOF_COND_COMPARATOR | (compare << 4), CMD_MODIFY_ORDER);
}
/* static */ bool AIOrder::SetOrderCompareValue(VehicleID vehicle_id, OrderPosition order_position, int32 value)
{
EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position));
EnforcePrecondition(false, order_position != ORDER_CURRENT && IsConditionalOrder(vehicle_id, order_position));
EnforcePrecondition(false, value >= 0 && value < 2048);
if (GetOrderCondition(vehicle_id, order_position) == OC_MAX_SPEED) value = value * 10 / 16;
int order_pos = AIOrderPositionToRealOrderPosition(vehicle_id, order_position);
return AIObject::DoCommand(0, vehicle_id | (order_pos << 20), MOF_COND_VALUE | (value << 4), CMD_MODIFY_ORDER);
}
/* static */ bool AIOrder::SetStopLocation(VehicleID vehicle_id, OrderPosition order_position, StopLocation stop_location)
{
EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position));
EnforcePrecondition(false, AIVehicle::GetVehicleType(vehicle_id) == AIVehicle::VT_RAIL);
EnforcePrecondition(false, IsGotoStationOrder(vehicle_id, order_position));
EnforcePrecondition(false, stop_location >= STOPLOCATION_NEAR && stop_location <= STOPLOCATION_FAR);
order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position);
int order_pos = AIOrderPositionToRealOrderPosition(vehicle_id, order_position);
uint32 p1 = vehicle_id | (order_pos << 20);
uint32 p2 = MOF_STOP_LOCATION | (stop_location << 4);
return AIObject::DoCommand(0, p1, p2, CMD_MODIFY_ORDER);
}
/* static */ bool AIOrder::SetOrderRefit(VehicleID vehicle_id, OrderPosition order_position, CargoID refit_cargo)
{
EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, order_position));
EnforcePrecondition(false, IsGotoStationOrder(vehicle_id, order_position) || (IsGotoDepotOrder(vehicle_id, order_position) && refit_cargo != CT_AUTO_REFIT));
EnforcePrecondition(false, AICargo::IsValidCargo(refit_cargo) || refit_cargo == CT_AUTO_REFIT || refit_cargo == CT_NO_REFIT);
uint32 p1 = vehicle_id;
uint32 p2 = refit_cargo | AIOrderPositionToRealOrderPosition(vehicle_id, AIOrder::ResolveOrderPosition(vehicle_id, order_position)) << 16;
return AIObject::DoCommand(0, p1, p2, CMD_ORDER_REFIT);
}
/* static */ bool AIOrder::AppendOrder(VehicleID vehicle_id, TileIndex destination, AIOrderFlags order_flags)
{
EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
EnforcePrecondition(false, AreOrderFlagsValid(destination, order_flags));
return InsertOrder(vehicle_id, (AIOrder::OrderPosition)::Vehicle::Get(vehicle_id)->GetNumManualOrders(), destination, order_flags);
}
/* static */ bool AIOrder::AppendConditionalOrder(VehicleID vehicle_id, OrderPosition jump_to)
{
EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, jump_to));
return InsertConditionalOrder(vehicle_id, (AIOrder::OrderPosition)::Vehicle::Get(vehicle_id)->GetNumManualOrders(), jump_to);
}
/* 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 == ORDER_CURRENT) order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position);
EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
EnforcePrecondition(false, order_position >= 0 && order_position <= ::Vehicle::Get(vehicle_id)->GetNumManualOrders());
EnforcePrecondition(false, AreOrderFlagsValid(destination, order_flags));
Order order;
OrderType ot = (order_flags & AIOF_GOTO_NEAREST_DEPOT) ? OT_GOTO_DEPOT : ::GetOrderTypeByTile(destination);
switch (ot) {
case OT_GOTO_DEPOT: {
OrderDepotTypeFlags odtf = (OrderDepotTypeFlags)(ODTFB_PART_OF_ORDERS | ((order_flags & AIOF_SERVICE_IF_NEEDED) ? ODTFB_SERVICE : 0));
OrderDepotActionFlags odaf = (OrderDepotActionFlags)(ODATF_SERVICE_ONLY | ((order_flags & AIOF_STOP_IN_DEPOT) ? ODATFB_HALT : 0));
if (order_flags & AIOF_GOTO_NEAREST_DEPOT) odaf |= ODATFB_NEAREST_DEPOT;
OrderNonStopFlags onsf = (OrderNonStopFlags)((order_flags & AIOF_NON_STOP_INTERMEDIATE) ? ONSF_NO_STOP_AT_INTERMEDIATE_STATIONS : ONSF_STOP_EVERYWHERE);
if (order_flags & AIOF_GOTO_NEAREST_DEPOT) {
order.MakeGoToDepot(0, odtf, onsf, odaf);
} else {
/* Check explicitly if the order is to a station (for aircraft) or
* to a depot (other vehicle types). */
if (::Vehicle::Get(vehicle_id)->type == VEH_AIRCRAFT) {
if (!::IsTileType(destination, MP_STATION)) return false;
order.MakeGoToDepot(::GetStationIndex(destination), odtf, onsf, odaf);
} else {
if (::IsTileType(destination, MP_STATION)) return false;
order.MakeGoToDepot(::GetDepotIndex(destination), odtf, onsf, odaf);
}
}
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));
order.SetStopLocation(OSL_PLATFORM_FAR_END);
break;
case OT_GOTO_WAYPOINT:
order.MakeGoToWaypoint(::GetStationIndex(destination));
break;
default:
return false;
}
order.SetNonStopType((OrderNonStopFlags)GB(order_flags, 0, 2));
int order_pos = AIOrderPositionToRealOrderPosition(vehicle_id, order_position);
return AIObject::DoCommand(0, vehicle_id | (order_pos << 20), order.Pack(), CMD_INSERT_ORDER);
}
/* static */ bool AIOrder::InsertConditionalOrder(VehicleID vehicle_id, OrderPosition order_position, OrderPosition jump_to)
{
/* IsValidVehicleOrder is not good enough because it does not allow appending. */
if (order_position == ORDER_CURRENT) order_position = AIOrder::ResolveOrderPosition(vehicle_id, order_position);
EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
EnforcePrecondition(false, order_position >= 0 && order_position <= ::Vehicle::Get(vehicle_id)->GetNumManualOrders());
EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, jump_to) && jump_to != ORDER_CURRENT);
Order order;
order.MakeConditional(jump_to);
int order_pos = AIOrderPositionToRealOrderPosition(vehicle_id, order_position);
return AIObject::DoCommand(0, vehicle_id | (order_pos << 20), 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));
int order_pos = AIOrderPositionToRealOrderPosition(vehicle_id, order_position);
return AIObject::DoCommand(0, vehicle_id, order_pos, CMD_DELETE_ORDER);
}
/* static */ bool AIOrder::SkipToOrder(VehicleID vehicle_id, OrderPosition next_order)
{
next_order = AIOrder::ResolveOrderPosition(vehicle_id, next_order);
EnforcePrecondition(false, IsValidVehicleOrder(vehicle_id, next_order));
int order_pos = AIOrderPositionToRealOrderPosition(vehicle_id, next_order);
return AIObject::DoCommand(0, vehicle_id, order_pos, CMD_SKIP_TO_ORDER);
}
/**
* Callback handler as SetOrderFlags 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 _DoCommandReturnSetOrderFlags(class AIInstance *instance)
{
AIObject::SetLastCommandRes(AIOrder::_SetOrderFlags());
AIInstance::DoCommandReturn(instance);
}
/* static */ bool AIOrder::_SetOrderFlags()
{
/* 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 SetOrderFlags() 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));
const Order *order = ::ResolveOrder(vehicle_id, order_position);
int order_pos = AIOrderPositionToRealOrderPosition(vehicle_id, order_position);
AIOrderFlags current = GetOrderFlags(vehicle_id, order_position);
EnforcePrecondition(false, (order_flags & AIOF_GOTO_NEAREST_DEPOT) == (current & AIOF_GOTO_NEAREST_DEPOT));
if ((current & AIOF_NON_STOP_FLAGS) != (order_flags & AIOF_NON_STOP_FLAGS)) {
return AIObject::DoCommand(0, vehicle_id | (order_pos << 20), (order_flags & AIOF_NON_STOP_FLAGS) << 4 | MOF_NON_STOP, CMD_MODIFY_ORDER, NULL, &::_DoCommandReturnSetOrderFlags);
}
switch (order->GetType()) {
case OT_GOTO_DEPOT:
if ((current & AIOF_DEPOT_FLAGS) != (order_flags & AIOF_DEPOT_FLAGS)) {
uint data = DA_ALWAYS_GO;
if (order_flags & AIOF_SERVICE_IF_NEEDED) data = DA_SERVICE;
if (order_flags & AIOF_STOP_IN_DEPOT) data = DA_STOP;
return AIObject::DoCommand(0, vehicle_id | (order_pos << 20), (data << 4) | MOF_DEPOT_ACTION, CMD_MODIFY_ORDER, NULL, &::_DoCommandReturnSetOrderFlags);
}
break;
case OT_GOTO_STATION:
if ((current & AIOF_UNLOAD_FLAGS) != (order_flags & AIOF_UNLOAD_FLAGS)) {
return AIObject::DoCommand(0, vehicle_id | (order_pos << 20), (order_flags & AIOF_UNLOAD_FLAGS) << 2 | MOF_UNLOAD, CMD_MODIFY_ORDER, NULL, &::_DoCommandReturnSetOrderFlags);
}
if ((current & AIOF_LOAD_FLAGS) != (order_flags & AIOF_LOAD_FLAGS)) {
return AIObject::DoCommand(0, vehicle_id | (order_pos << 20), (order_flags & AIOF_LOAD_FLAGS) >> 1 | MOF_LOAD, CMD_MODIFY_ORDER, NULL, &::_DoCommandReturnSetOrderFlags);
}
break;
default: break;
}
assert(GetOrderFlags(vehicle_id, order_position) == order_flags);
return true;
}
/* static */ bool AIOrder::SetOrderFlags(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::_SetOrderFlags();
}
/* 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));
int order_pos_move = AIOrderPositionToRealOrderPosition(vehicle_id, order_position_move);
int order_pos_target = AIOrderPositionToRealOrderPosition(vehicle_id, order_position_target);
return AIObject::DoCommand(0, vehicle_id, order_pos_move | (order_pos_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 | CO_COPY << 30, main_vehicle_id, 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 | CO_SHARE << 30, main_vehicle_id, CMD_CLONE_ORDER);
}
/* static */ bool AIOrder::UnshareOrders(VehicleID vehicle_id)
{
EnforcePrecondition(false, AIVehicle::IsValidVehicle(vehicle_id));
return AIObject::DoCommand(0, vehicle_id | CO_UNSHARE << 30, 0, CMD_CLONE_ORDER);
}

View File

@@ -0,0 +1,561 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_order.hpp Everything to query and build orders. */
#ifndef SCRIPT_ORDER_HPP
#define SCRIPT_ORDER_HPP
#include "script_error.hpp"
/**
* Class that handles all order related functions.
*/
class AIOrder : public AIObject {
public:
/**
* 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_ERROR_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_ERROR_TOO_FAR_FROM_PREVIOUS_DESTINATION]
};
/**
* 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,
/** Stop in the depot instead of only go there for servicing; only for depots. */
AIOF_STOP_IN_DEPOT = 1 << 3,
/** Go to nearest depot. */
AIOF_GOTO_NEAREST_DEPOT = 1 << 8,
/** 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,
/** All flags related to depots. */
AIOF_DEPOT_FLAGS = AIOF_SERVICE_IF_NEEDED | AIOF_STOP_IN_DEPOT | AIOF_GOTO_NEAREST_DEPOT,
/** For marking invalid order flags */
AIOF_INVALID = 0xFFFF,
};
/**
* All conditions a conditional order can depend on.
*/
enum OrderCondition {
/* Order _is_ important, as it's based on OrderConditionVariable in order_type.h. */
OC_LOAD_PERCENTAGE, ///< Skip based on the amount of load, value is in tons.
OC_RELIABILITY, ///< Skip based on the reliability, value is percent (0..100).
OC_MAX_SPEED, ///< Skip based on the maximum speed, value is in OpenTTD's internal speed unit, see AIEngine::GetMaxSpeed.
OC_AGE, ///< Skip based on the age, value is in years.
OC_REQUIRES_SERVICE, ///< Skip when the vehicle requires service, no value.
OC_UNCONDITIONALLY, ///< Always skip, no compare function, no value.
OC_REMAINING_LIFETIME, ///< Skip based on the remaining lifetime
OC_INVALID = -1, ///< An invalid condition, do not use.
};
/**
* Comparators for conditional orders.
*/
enum CompareFunction {
/* Order _is_ important, as it's based on OrderConditionComparator in order_type.h. */
CF_EQUALS, ///< Skip if both values are equal
CF_NOT_EQUALS, ///< Skip if both values are not equal
CF_LESS_THAN, ///< Skip if the value is less than the limit
CF_LESS_EQUALS, ///< Skip if the value is less or equal to the limit
CF_MORE_THAN, ///< Skip if the value is more than the limit
CF_MORE_EQUALS, ///< Skip if the value is more or equal to the limit
CF_IS_TRUE, ///< Skip if the variable is true
CF_IS_FALSE, ///< Skip if the variable is false
CF_INVALID = -1, ///< Invalid compare function, do not use.
};
/**
* Index in the list of orders for a vehicle. The first order has index 0, the second
* order index 1, etc. The current order can be queried by using ORDER_CURRENT. Do not
* use ORDER_INVALID yourself, it's used as return value by for example ResolveOrderPosition.
* @note Automatic orders are hidden from AIs, so OrderPosition 0 will always be the first
* manual order.
*/
enum OrderPosition {
ORDER_CURRENT = 0xFF, ///< Constant that gets resolved to the current order.
ORDER_INVALID = -1, ///< An invalid order.
};
/** Where to stop trains in a station that's longer than the train */
enum StopLocation {
STOPLOCATION_NEAR, ///< Stop the train as soon as it's completely in the station
STOPLOCATION_MIDDLE, ///< Stop the train in the middle of the station
STOPLOCATION_FAR, ///< Stop the train at the far end of the station
STOPLOCATION_INVALID = -1, ///< An invalid stop location
};
/**
* 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);
/**
* Checks whether the given order is a goto-station order.
* @param vehicle_id The vehicle to check.
* @param order_position The order index to check.
* @pre IsValidVehicleOrder(vehicle_id, order_position).
* @return True if and only if the order is a goto-station order.
*/
static bool IsGotoStationOrder(VehicleID vehicle_id, OrderPosition order_position);
/**
* Checks whether the given order is a goto-depot order.
* @param vehicle_id The vehicle to check.
* @param order_position The order index to check.
* @pre IsValidVehicleOrder(vehicle_id, order_position).
* @return True if and only if the order is a goto-depot order.
*/
static bool IsGotoDepotOrder(VehicleID vehicle_id, OrderPosition order_position);
/**
* Checks whether the given order is a goto-waypoint order.
* @param vehicle_id The vehicle to check.
* @param order_position The order index to check.
* @pre IsValidVehicleOrder(vehicle_id, order_position).
* @return True if and only if the order is a goto-waypoint order.
*/
static bool IsGotoWaypointOrder(VehicleID vehicle_id, OrderPosition order_position);
/**
* Checks whether the given order is a conditional order.
* @param vehicle_id The vehicle to check.
* @param order_position The order index to check.
* @pre order_position != ORDER_CURRENT && IsValidVehicleOrder(vehicle_id, order_position).
* @return True if and only if the order is a conditional order.
*/
static bool IsConditionalOrder(VehicleID vehicle_id, OrderPosition order_position);
/**
* Checks whether the given order is a void order.
* A void order is an order that used to be a goto station, depot or waypoint order but
* its destination got removed. In OpenTTD these orders as shown as "(Invalid Order)"
* in the order list of a vehicle.
* @param vehicle_id The vehicle to check.
* @param order_position The order index to check.
* @pre order_position != ORDER_CURRENT && IsValidVehicleOrder(vehicle_id, order_position).
* @return True if and only if the order is a void order.
*/
static bool IsVoidOrder(VehicleID vehicle_id, OrderPosition order_position);
/**
* Checks whether the given order has a valid refit cargo.
* @param vehicle_id The vehicle to check.
* @param order_position The order index to check.
* @pre order_position != ORDER_CURRENT && IsValidVehicleOrder(vehicle_id, order_position).
* @return True if and only if the order is has a valid refit cargo.
*/
static bool IsRefitOrder(VehicleID vehicle_id, OrderPosition order_position);
/**
* Checks whether the current order is part of the orderlist.
* @param vehicle_id The vehicle to check.
* @pre AIVehicle::IsValidVehicle(vehicle_id).
* @return True if and only if the current order is part of the order list.
* @note If the order is a non-'non-stop' order, and the vehicle is currently
* (un)loading at a station that is not the final destination, this function
* will still return true.
*/
static bool IsCurrentOrderPartOfOrderList(VehicleID vehicle_id);
/**
* Resolves the given order index to the correct index for the given vehicle.
* If the order index was ORDER_CURRENT 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 ORDER_INVALID.
* @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);
/**
* Checks whether the given combination of condition and compare function is valid.
* @param condition The condition to check.
* @param compare The compare function to check.
* @return True if and only if the combination of condition and compare function is valid.
*/
static bool IsValidConditionalOrder(OrderCondition condition, CompareFunction compare);
/**
* 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).
* @pre order_position == ORDER_CURRENT || !IsConditionalOrder(vehicle_id, order_position).
* @note Giving ORDER_CURRENT 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).
* @pre order_position == ORDER_CURRENT || (!IsConditionalOrder(vehicle_id, order_position) && !IsVoidOrder(vehicle_id, order_position)).
* @note Giving ORDER_CURRENT 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);
/**
* Gets the OrderPosition to jump to if the check succeeds of the given order for the given vehicle.
* @param vehicle_id The vehicle to get the OrderPosition for.
* @param order_position The order to get the OrderPosition for.
* @pre IsValidVehicleOrder(vehicle_id, order_position).
* @pre order_position != ORDER_CURRENT && IsConditionalOrder(vehicle_id, order_position).
* @return The target of the conditional jump.
*/
static OrderPosition GetOrderJumpTo(VehicleID vehicle_id, OrderPosition order_position);
/**
* Gets the OrderCondition of the given order for the given vehicle.
* @param vehicle_id The vehicle to get the condition type for.
* @param order_position The order to get the condition type for.
* @pre IsValidVehicleOrder(vehicle_id, order_position).
* @pre order_position != ORDER_CURRENT && IsConditionalOrder(vehicle_id, order_position).
* @return The OrderCondition of the order.
*/
static OrderCondition GetOrderCondition(VehicleID vehicle_id, OrderPosition order_position);
/**
* Gets the CompareFunction of the given order for the given vehicle.
* @param vehicle_id The vehicle to get the compare function for.
* @param order_position The order to get the compare function for.
* @pre IsValidVehicleOrder(vehicle_id, order_position).
* @pre order_position != ORDER_CURRENT && IsConditionalOrder(vehicle_id, order_position).
* @return The CompareFunction of the order.
*/
static CompareFunction GetOrderCompareFunction(VehicleID vehicle_id, OrderPosition order_position);
/**
* Gets the value to compare against of the given order for the given vehicle.
* @param vehicle_id The vehicle to get the value for.
* @param order_position The order to get the value for.
* @pre IsValidVehicleOrder(vehicle_id, order_position).
* @pre order_position != ORDER_CURRENT && IsConditionalOrder(vehicle_id, order_position).
* @return The value to compare against of the order.
*/
static int32 GetOrderCompareValue(VehicleID vehicle_id, OrderPosition order_position);
/**
* Gets the stoplocation of the given order for the given train.
* @param vehicle_id The vehicle to get the value for.
* @param order_position The order to get the value for.
* @pre IsValidVehicleOrder(vehicle_id, order_position).
* @pre AIVehicle::GetVehicleType(vehicle_id) == AIVehicle::VT_RAIL.
* @pre IsGotoStationOrder(vehicle_id, order_position).
* @return The relative position where the train will stop inside a station.
*/
static StopLocation GetStopLocation(VehicleID vehicle_id, OrderPosition order_position);
/**
* Gets the refit cargo type of the given order for the given vehicle.
* @param vehicle_id The vehicle to get the refit cargo for.
* @param order_position The order to get the refit cargo for.
* @pre IsValidVehicleOrder(vehicle_id, order_position).
* @pre order_position == ORDER_CURRENT || IsGotoStationOrder(vehicle_id, order_position) || IsGotoDepotOrder(vehicle_id, order_position).
* @note Giving ORDER_CURRENT 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 refit cargo of the order or CT_NO_REFIT if no refit is set.
*/
static CargoID GetOrderRefit(VehicleID vehicle_id, OrderPosition order_position);
/**
* Sets the OrderPosition to jump to if the check succeeds of the given order for the given vehicle.
* @param vehicle_id The vehicle to set the OrderPosition for.
* @param order_position The order to set the OrderPosition for.
* @param jump_to The order to jump to if the check succeeds.
* @pre IsValidVehicleOrder(vehicle_id, order_position).
* @pre IsValidVehicleOrder(vehicle_id, jump_to).
* @pre order_position != ORDER_CURRENT && IsConditionalOrder(vehicle_id, order_position).
* @return Whether the order has been/can be changed.
*/
static bool SetOrderJumpTo(VehicleID vehicle_id, OrderPosition order_position, OrderPosition jump_to);
/**
* Sets the OrderCondition of the given order for the given vehicle.
* @param vehicle_id The vehicle to set the condition type for.
* @param order_position The order to set the condition type for.
* @param condition The condition to compare on.
* @pre IsValidVehicleOrder(vehicle_id, order_position).
* @pre order_position != ORDER_CURRENT && IsConditionalOrder(vehicle_id, order_position).
* @pre condition >= OC_LOAD_PERCENTAGE && condition <= OC_UNCONDITIONALLY.
* @return Whether the order has been/can be changed.
*/
static bool SetOrderCondition(VehicleID vehicle_id, OrderPosition order_position, OrderCondition condition);
/**
* Sets the CompareFunction of the given order for the given vehicle.
* @param vehicle_id The vehicle to set the compare function for.
* @param order_position The order to set the compare function for.
* @param compare The new compare function of the order.
* @pre IsValidVehicleOrder(vehicle_id, order_position).
* @pre order_position != ORDER_CURRENT && IsConditionalOrder(vehicle_id, order_position).
* @pre compare >= CF_EQUALS && compare <= CF_IS_FALSE.
* @return Whether the order has been/can be changed.
*/
static bool SetOrderCompareFunction(VehicleID vehicle_id, OrderPosition order_position, CompareFunction compare);
/**
* Sets the value to compare against of the given order for the given vehicle.
* @param vehicle_id The vehicle to set the value for.
* @param order_position The order to set the value for.
* @param value The value to compare against.
* @pre IsValidVehicleOrder(vehicle_id, order_position).
* @pre order_position != ORDER_CURRENT && IsConditionalOrder(vehicle_id, order_position).
* @pre value >= 0 && value < 2048.
* @return Whether the order has been/can be changed.
*/
static bool SetOrderCompareValue(VehicleID vehicle_id, OrderPosition order_position, int32 value);
/**
* Sets the stoplocation of the given order for the given train.
* @param vehicle_id The vehicle to get the value for.
* @param order_position The order to get the value for.
* @param stop_location The relative position where a train will stop inside a station.
* @pre IsValidVehicleOrder(vehicle_id, order_position).
* @pre AIVehicle::GetVehicleType(vehicle_id) == AIVehicle::VT_RAIL.
* @pre IsGotoStationOrder(vehicle_id, order_position).
* @pre stop_location >= STOPLOCATION_NEAR && stop_location <= STOPLOCATION_FAR
* @return Whether the order has been/can be changed.
*/
static bool SetStopLocation(VehicleID vehicle_id, OrderPosition order_position, StopLocation stop_location);
/**
* Sets the refit cargo type of the given order for the given vehicle.
* @param vehicle_id The vehicle to set the refit cargo for.
* @param order_position The order to set the refit cargo for.
* @param refit_cargo The cargo to refit to. The refit can be cleared by passing CT_NO_REFIT.
* @pre IsValidVehicleOrder(vehicle_id, order_position).
* @pre IsGotoStationOrder(vehicle_id, order_position) || (IsGotoDepotOrder(vehicle_id, order_position) && refit_cargo != CT_AUTO_REFIT).
* @pre AICargo::IsValidCargo(refit_cargo) || refit_cargo == CT_AUTO_REFIT || refit_cargo == CT_NO_REFIT
* @return Whether the order has been/can be changed.
*/
static bool SetOrderRefit(VehicleID vehicle_id, OrderPosition order_position, CargoID refit_cargo);
/**
* 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_TOO_MANY
* @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);
/**
* Appends a conditional order to the end of the vehicle's order list.
* @param vehicle_id The vehicle to append the order to.
* @param jump_to The OrderPosition to jump to if the condition is true.
* @pre AIVehicle::IsValidVehicle(vehicle_id).
* @pre IsValidVehicleOrder(vehicle_id, jump_to).
* @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
* @exception AIOrder::ERR_ORDER_TOO_MANY
* @return True if and only if the order was appended.
*/
static bool AppendConditionalOrder(VehicleID vehicle_id, OrderPosition jump_to);
/**
* 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_TOO_MANY
* @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);
/**
* Appends a conditional 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 jump_to The OrderPosition to jump to if the condition is true.
* @pre IsValidVehicleOrder(vehicle_id, order_position).
* @pre IsValidVehicleOrder(vehicle_id, jump_to).
* @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
* @exception AIOrder::ERR_ORDER_TOO_MANY
* @return True if and only if the order was inserted.
*/
static bool InsertConditionalOrder(VehicleID vehicle_id, OrderPosition order_position, OrderPosition jump_to);
/**
* 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 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_AI_DOCS
/**
* Internal function to help SetOrderFlags.
*/
static bool _SetOrderFlags();
#endif /* DOXYGEN_AI_DOCS */
/**
* 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).
* @pre (order_flags & AIOF_GOTO_NEAREST_DEPOT) == (GetOrderFlags(vehicle_id, order_position) & AIOF_GOTO_NEAREST_DEPOT).
* @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
* @return True if and only if the order was changed.
*/
static bool SetOrderFlags(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);
/**
* Make a vehicle execute next_order instead of its current order.
* @param vehicle_id The vehicle that should skip some orders.
* @param next_order The order the vehicle should skip to.
* @pre IsValidVehicleOrder(vehicle_id, next_order).
* @exception AIError::ERR_OWNED_BY_ANOTHER_COMPANY
* @return True if and only the current order was changed.
*/
static bool SkipToOrder(VehicleID vehicle_id, OrderPosition next_order);
/**
* 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_TOO_MANY
* @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 /* SCRIPT_ORDER_HPP */

View File

@@ -0,0 +1,494 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_rail.cpp Implementation of AIRail. */
#include "../../stdafx.h"
#include "script_rail.hpp"
#include "script_map.hpp"
#include "script_station.hpp"
#include "script_industrytype.hpp"
#include "script_cargo.hpp"
#include "../../debug.h"
#include "../../station_base.h"
#include "../../company_func.h"
#include "../../newgrf.h"
#include "../../newgrf_generic.h"
#include "../../newgrf_station.h"
#include "../../strings_func.h"
/* static */ char *AIRail::GetName(RailType rail_type)
{
if (!IsRailTypeAvailable(rail_type)) return NULL;
static const int len = 64;
char *railtype_name = MallocT<char>(len);
::GetString(railtype_name, GetRailTypeInfo((::RailType)rail_type)->strings.menu_text, &railtype_name[len - 1]);
return railtype_name;
}
/* static */ bool AIRail::IsRailTile(TileIndex tile)
{
if (!::IsValidTile(tile)) return false;
return (::IsTileType(tile, MP_RAILWAY) && !::IsRailDepot(tile)) ||
(::HasStationTileRail(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 ::IsRailStationTile(tile);
}
/* static */ bool AIRail::IsRailWaypointTile(TileIndex tile)
{
if (!::IsValidTile(tile)) return false;
return ::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)::GetRailStationTrackBits(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, StationID station_id)
{
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()));
EnforcePrecondition(false, station_id == AIStation::STATION_NEW || station_id == AIStation::STATION_JOIN_ADJACENT || AIStation::IsValidStation(station_id));
uint32 p1 = GetCurrentRailType() | (platform_length << 16) | (num_platforms << 8);
if (direction == RAILTRACK_NW_SE) p1 |= (1 << 4);
if (station_id != AIStation::STATION_JOIN_ADJACENT) p1 |= (1 << 24);
return AIObject::DoCommand(tile, p1, (AIStation::IsValidStation(station_id) ? station_id : INVALID_STATION) << 16, CMD_BUILD_RAIL_STATION);
}
/* static */ bool AIRail::BuildNewGRFRailStation(TileIndex tile, RailTrack direction, uint num_platforms, uint platform_length, StationID station_id, 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()));
EnforcePrecondition(false, station_id == AIStation::STATION_NEW || station_id == AIStation::STATION_JOIN_ADJACENT || AIStation::IsValidStation(station_id));
EnforcePrecondition(false, AICargo::IsValidCargo(cargo_id));
EnforcePrecondition(false, source_industry == AIIndustryType::INDUSTRYTYPE_UNKNOWN || source_industry == AIIndustryType::INDUSTRYTYPE_TOWN || AIIndustryType::IsValidIndustryType(source_industry));
EnforcePrecondition(false, goal_industry == AIIndustryType::INDUSTRYTYPE_UNKNOWN || goal_industry == AIIndustryType::INDUSTRYTYPE_TOWN || AIIndustryType::IsValidIndustryType(goal_industry));
uint32 p1 = GetCurrentRailType() | (platform_length << 16) | (num_platforms << 8);
if (direction == RAILTRACK_NW_SE) p1 |= 1 << 4;
if (station_id != AIStation::STATION_JOIN_ADJACENT) p1 |= (1 << 24);
const GRFFile *file;
uint16 res = GetAiPurchaseCallbackResult(GSF_STATIONS, 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 = (AIStation::IsValidStation(station_id) ? station_id : INVALID_STATION) << 16;
if (res != CALLBACK_FAILED) {
int index = 0;
const StationSpec *spec = StationClass::GetByGrf(file->grfid, res, &index);
if (spec == NULL) {
DEBUG(grf, 1, "%s returned an invalid station ID for 'AI construction/purchase selection (18)' callback", file->filename);
} else {
p2 |= spec->cls_id | index << 8;
}
}
return AIObject::DoCommand(tile, p1, p2, CMD_BUILD_RAIL_STATION);
}
/* static */ bool AIRail::BuildRailWaypoint(TileIndex tile)
{
EnforcePrecondition(false, ::IsValidTile(tile));
EnforcePrecondition(false, IsRailTile(tile));
EnforcePrecondition(false, GetRailTracks(tile) == RAILTRACK_NE_SW || GetRailTracks(tile) == RAILTRACK_NW_SE);
EnforcePrecondition(false, IsRailTypeAvailable(GetCurrentRailType()));
return AIObject::DoCommand(tile, GetCurrentRailType() | (GetRailTracks(tile) == RAILTRACK_NE_SW ? AXIS_X : AXIS_Y) << 4 | 1 << 8 | 1 << 16, STAT_CLASS_WAYP | INVALID_STATION << 16, CMD_BUILD_RAIL_WAYPOINT);
}
/* static */ bool AIRail::RemoveRailWaypointTileRectangle(TileIndex tile, TileIndex tile2, bool keep_rail)
{
EnforcePrecondition(false, ::IsValidTile(tile));
EnforcePrecondition(false, ::IsValidTile(tile2));
return AIObject::DoCommand(tile, tile2, keep_rail ? 1 : 0, CMD_REMOVE_FROM_RAIL_WAYPOINT);
}
/* static */ bool AIRail::RemoveRailStationTileRectangle(TileIndex tile, TileIndex tile2, bool keep_rail)
{
EnforcePrecondition(false, ::IsValidTile(tile));
EnforcePrecondition(false, ::IsValidTile(tile2));
return AIObject::DoCommand(tile, tile2, keep_rail ? 1 : 0, CMD_REMOVE_FROM_RAIL_STATION);
}
/* static */ uint AIRail::GetRailTracks(TileIndex tile)
{
if (!IsRailTile(tile)) return RAILTRACK_INVALID;
if (IsRailStationTile(tile) || IsRailWaypointTile(tile)) return ::TrackToTrackBits(::GetRailStationTrack(tile));
if (IsLevelCrossingTile(tile)) return ::GetCrossingRailBits(tile);
if (IsRailDepotTile(tile)) return ::TRACK_BIT_NONE;
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, ::IsPlainRailTile(tile) || ::IsLevelCrossingTile(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 != 0) {
*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 != 0) {
*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 == 0) {
*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 == 0) {
*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) | 1 << 8;
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, ::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);
}
/* static */ Money AIRail::GetBuildCost(RailType railtype, BuildType build_type)
{
if (!AIRail::IsRailTypeAvailable(railtype)) return -1;
switch (build_type) {
case BT_TRACK: return ::RailBuildCost((::RailType)railtype);
case BT_SIGNAL: return ::GetPrice(PR_BUILD_SIGNALS, 1, NULL);
case BT_DEPOT: return ::GetPrice(PR_BUILD_DEPOT_TRAIN, 1, NULL);
case BT_STATION: return ::GetPrice(PR_BUILD_STATION_RAIL, 1, NULL) + ::GetPrice(PR_BUILD_STATION_RAIL_LENGTH, 1, NULL);
case BT_WAYPOINT: return ::GetPrice(PR_BUILD_WAYPOINT_RAIL, 1, NULL);
default: return -1;
}
}
/* static */ int32 AIRail::GetMaxSpeed(RailType railtype)
{
if (!AIRail::IsRailTypeAvailable(railtype)) return -1;
return ::GetRailTypeInfo((::RailType)railtype)->max_speed;
}

View File

@@ -0,0 +1,468 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_rail.hpp Everything to query and build rails. */
#ifndef SCRIPT_RAIL_HPP
#define SCRIPT_RAIL_HPP
#include "script_tile.hpp"
/**
* Class that handles all rail related functions.
*/
class AIRail : public AIObject {
public:
/**
* 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_ERROR_CROSSING_ON_ONEWAY_ROAD]
/** Track not suitable for signals */
ERR_UNSUITABLE_TRACK, // [STR_ERROR_NO_SUITABLE_RAILROAD_TRACK]
/** This railtype cannot have crossings */
ERR_RAILTYPE_DISALLOWS_CROSSING, // [STR_ERROR_CROSSING_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.
};
/**
* Types of rail-related objects in the game.
*/
enum BuildType {
BT_TRACK, ///< Build a track
BT_SIGNAL, ///< Build a signal
BT_DEPOT, ///< Build a depot
BT_STATION, ///< Build a station
BT_WAYPOINT, ///< Build a rail waypoint
};
/**
* Get the name of a rail type.
* @param rail_type The rail type to get the name of.
* @pre IsRailTypeAvailable(rail_type).
* @return The name the rail type has.
* @note Since there is no string with only the name of the track, the text which
* is shown in the dropdown where you can chose a track type is returned. This
* means that the name could be something like "Maglev construction" instead
* of just "Maglev".
*/
static char *GetName(RailType rail_type);
/**
* 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 successfully.
*/
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 station_id The station to join, AIStation::STATION_NEW or AIStation::STATION_JOIN_ADJACENT.
* @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.
* @pre station_id == AIStation::STATION_NEW || station_id == AIStation::STATION_JOIN_ADJACENT || AIStation::IsValidStation(station_id).
* @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, StationID station_id);
/**
* 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 station_id The station to join, AIStation::STATION_NEW or AIStation::STATION_JOIN_ADJACENT.
* @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, AIIndustryType::INDUSTRYTYPE_UNKNOWN or AIIndustryType::INDUSTRYTYPE_TOWN.
* @param goal_industry The IndustryType of the industry you'll transport goods to, AIIndustryType::INDUSTRYTYPE_UNKNOWN or AIIndustryType::INDUSTRYTYPE_TOWN.
* @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.
* @pre station_id == AIStation::STATION_NEW || station_id == AIStation::STATION_JOIN_ADJACENT || AIStation::IsValidStation(station_id).
* @pre AICargo::IsValidCargo(cargo_type)
* @pre source_industry == AIIndustryType::INDUSTRYTYPE_UNKNOWN || source_industry == AIIndustryType::INDUSTRYTYPE_TOWN || AIIndustryType::IsValidIndustryType(source_industry).
* @pre goal_industry == AIIndustryType::INDUSTRYTYPE_UNKNOWN || goal_industry == AIIndustryType::INDUSTRYTYPE_TOWN || AIIndustryType::IsValidIndustryType(goal_industry).
* @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, StationID station_id, CargoID cargo_id, IndustryType source_industry, IndustryType goal_industry, int distance, bool source_station);
/**
* Build a rail waypoint.
* @param tile Place to build the waypoint.
* @pre AIMap::IsValidTile(tile).
* @pre IsRailTile(tile).
* @pre GetRailTracks(tile) == RAILTRACK_NE_SW || GetRailTracks(tile) == RAILTRACK_NW_SE.
* @pre IsRailTypeAvailable(GetCurrentRailType()).
* @exception AIError::ERR_FLAT_LAND_REQUIRED
* @return Whether the rail waypoint has been/can be build or not.
*/
static bool BuildRailWaypoint(TileIndex tile);
/**
* Remove all rail waypoint pieces within a rectangle on the map.
* @param tile One corner of the rectangle to clear.
* @param tile2 The oppposite corner.
* @param keep_rail Whether to keep the rail after removal.
* @pre IsValidTile(tile).
* @pre IsValidTile(tile2).
* @return Whether at least one tile has been/can be cleared or not.
*/
static bool RemoveRailWaypointTileRectangle(TileIndex tile, TileIndex tile2, bool keep_rail);
/**
* Remove all rail station platform pieces within a rectangle on the map.
* @param tile One corner of the rectangle to clear.
* @param tile2 The oppposite corner.
* @param keep_rail Whether to keep the rail after removal.
* @pre IsValidTile(tile).
* @pre IsValidTile(tile2).
* @return Whether at least one tile has been/can be cleared or not.
*/
static bool RemoveRailStationTileRectangle(TileIndex tile, TileIndex tile2, bool keep_rail);
/**
* Get all RailTracks on the given tile.
* @note A depot has no railtracks.
* @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
* @note Construction will fail if an obstacle is found between the start and end tiles.
* @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' facing 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);
/**
* Get the baseprice of building a rail-related object.
* @param railtype the railtype that is build (on)
* @param build_type the type of object to build
* @pre IsRailTypeAvailable(railtype)
* @return The baseprice of building the given object.
*/
static Money GetBuildCost(RailType railtype, BuildType build_type);
/**
* Get the maximum speed of trains running on this railtype.
* @param railtype The railtype to get the maximum speed of.
* @pre IsRailTypeAvailable(railtype)
* @return The maximum speed trains can run on this railtype
* or 0 if there is no limit.
* @note The speed is in OpenTTD's internal speed unit.
* This is mph / 1.6, which is roughly km/h.
* To get km/h multiply this number by 1.00584.
*/
static int32 GetMaxSpeed(RailType railtype);
};
#endif /* SCRIPT_RAIL_HPP */

View File

@@ -0,0 +1,22 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_railtypelist.cpp Implementation of AIRailTypeList and friends. */
#include "../../stdafx.h"
#include "script_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);
}
}

View File

@@ -0,0 +1,26 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_railtypelist.hpp List all available railtypes. */
#ifndef SCRIPT_RAILTYPELIST_HPP
#define SCRIPT_RAILTYPELIST_HPP
#include "script_list.hpp"
/**
* Creates a list of all available railtypes.
* @ingroup AIList
*/
class AIRailTypeList : public AIList {
public:
AIRailTypeList();
};
#endif /* SCRIPT_RAILTYPELIST_HPP */

View File

@@ -0,0 +1,583 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_road.cpp Implementation of AIRoad. */
#include "../../stdafx.h"
#include "script_map.hpp"
#include "script_station.hpp"
#include "script_cargo.hpp"
#include "../../station_base.h"
#include "../../company_func.h"
#include "../../script/squirrel_helper_type.hpp"
/* static */ AIRoad::RoadVehicleType AIRoad::GetRoadVehicleTypeForCargo(CargoID cargo_type)
{
return AICargo::HasCargoClass(cargo_type, AICargo::CC_PASSENGERS) ? ROADVEHTYPE_BUS : ROADVEHTYPE_TRUCK;
}
/* 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;
if (!IsRoadTypeAvailable(GetCurrentRoadType())) 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)
{
/* Steep slopes behave the same as slopes with one corner raised. */
if (IsSteepSlope(slope)) {
slope = SlopeWithOneCornerRaised(GetHighestSlopeCorner(slope));
}
/* 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 ROAD_N:
case ROAD_E:
case ROAD_S:
/* Cannot build anything with a turn from the low side. */
return 0;
case ROAD_X:
case ROAD_Y:
/* 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_E) && !(existing_roadbits & ROAD_W)) ? 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_E) ? 0 : 1;
}
case SLOPE_SW:
/* A slope similar to a SLOPE_SW. */
switch (new_roadbits) {
case ROAD_N:
case ROAD_E:
/* Cannot build anything with a turn from the low side. */
return 0;
case ROAD_X:
/* 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 rts The road type we want to know reachability for
* @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;
if (!IsRoadTypeAvailable(GetCurrentRoadType())) return false;
::RoadTypes rts = ::RoadTypeToRoadTypes((::RoadType)GetCurrentRoadType());
int32 neighbour = 0;
if (TileX(tile) > 0 && NeighbourHasReachableRoad(rts, tile, DIAGDIR_NE)) neighbour++;
if (NeighbourHasReachableRoad(rts, tile, DIAGDIR_SE)) neighbour++;
if (NeighbourHasReachableRoad(rts, tile, DIAGDIR_SW)) neighbour++;
if (TileY(tile) > 0 && 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);
EnforcePrecondition(false, IsRoadTypeAvailable(GetCurrentRoadType()));
return AIObject::DoCommand(start, end, (::TileY(start) != ::TileY(end) ? 4 : 0) | (((start < end) == !full) ? 1 : 2) | (AIObject::GetRoadType() << 3) | ((one_way ? 1 : 0) << 5) | 1 << 6, 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));
EnforcePrecondition(false, IsRoadTypeAvailable(GetCurrentRoadType()));
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::_BuildRoadStationInternal(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, bool drive_through, StationID station_id)
{
EnforcePrecondition(false, tile != front);
EnforcePrecondition(false, ::IsValidTile(tile));
EnforcePrecondition(false, ::IsValidTile(front));
EnforcePrecondition(false, ::TileX(tile) == ::TileX(front) || ::TileY(tile) == ::TileY(front));
EnforcePrecondition(false, station_id == AIStation::STATION_NEW || station_id == AIStation::STATION_JOIN_ADJACENT || AIStation::IsValidStation(station_id));
EnforcePrecondition(false, road_veh_type == ROADVEHTYPE_BUS || road_veh_type == ROADVEHTYPE_TRUCK);
EnforcePrecondition(false, IsRoadTypeAvailable(GetCurrentRoadType()));
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);
}
uint p2 = station_id == AIStation::STATION_JOIN_ADJACENT ? 0 : 32;
p2 |= drive_through ? 2 : 0;
p2 |= road_veh_type == ROADVEHTYPE_TRUCK ? 1 : 0;
p2 |= ::RoadTypeToRoadTypes(AIObject::GetRoadType()) << 2;
p2 |= entrance_dir << 6;
p2 |= (AIStation::IsValidStation(station_id) ? station_id : INVALID_STATION) << 16;
return AIObject::DoCommand(tile, 1 | 1 << 8, p2, CMD_BUILD_ROAD_STOP);
}
/* static */ bool AIRoad::BuildRoadStation(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, StationID station_id)
{
return _BuildRoadStationInternal(tile, front, road_veh_type, false, station_id);
}
/* static */ bool AIRoad::BuildDriveThroughRoadStation(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, StationID station_id)
{
return _BuildRoadStationInternal(tile, front, road_veh_type, true, station_id);
}
/* 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));
EnforcePrecondition(false, IsRoadTypeAvailable(GetCurrentRoadType()));
return AIObject::DoCommand(start, end, (::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));
EnforcePrecondition(false, IsRoadTypeAvailable(GetCurrentRoadType()));
return AIObject::DoCommand(start, end, (::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, 1 | 1 << 8, GetRoadStopType(tile), CMD_REMOVE_ROAD_STOP);
}
/* static */ Money AIRoad::GetBuildCost(RoadType roadtype, BuildType build_type)
{
if (!AIRoad::IsRoadTypeAvailable(roadtype)) return -1;
switch (build_type) {
case BT_ROAD: return ::GetPrice(PR_BUILD_ROAD, 1, NULL);
case BT_DEPOT: return ::GetPrice(PR_BUILD_DEPOT_ROAD, 1, NULL);
case BT_BUS_STOP: return ::GetPrice(PR_BUILD_STATION_BUS, 1, NULL);
case BT_TRUCK_STOP: return ::GetPrice(PR_BUILD_STATION_TRUCK, 1, NULL);
default: return -1;
}
}

View File

@@ -0,0 +1,486 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_road.hpp Everything to query and build roads. */
#ifndef SCRIPT_ROAD_HPP
#define SCRIPT_ROAD_HPP
#include "script_tile.hpp"
/**
* Class that handles all road related functions.
*/
class AIRoad : public AIObject {
public:
/**
* 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_ERROR_ROAD_WORKS_IN_PROGRESS]
/** Drive through is in the wrong direction */
ERR_ROAD_DRIVE_THROUGH_WRONG_DIRECTION, // [STR_ERROR_DRIVE_THROUGH_DIRECTION]
/** Drive through roads can't be build on town owned roads */
ERR_ROAD_CANNOT_BUILD_ON_TOWN_ROAD, // [STR_ERROR_DRIVE_THROUGH_ON_TOWN_ROAD]
/** One way roads can't have junctions */
ERR_ROAD_ONE_WAY_ROADS_CANNOT_HAVE_JUNCTIONS, // [STR_ERROR_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.
};
/**
* Type of road station.
*/
enum RoadVehicleType {
ROADVEHTYPE_BUS, ///< Build objects useable for busses and passenger trams
ROADVEHTYPE_TRUCK, ///< Build objects useable for trucks and cargo trams
};
/**
* Types of road-related objects in the game.
*/
enum BuildType {
BT_ROAD, ///< Build a piece of road
BT_DEPOT, ///< Build a road depot
BT_BUS_STOP, ///< Build a bus stop
BT_TRUCK_STOP, ///< Build a truck stop
};
/**
* Determines whether a busstop or a truckstop is needed to transport a certain cargo.
* @param cargo_type The cargo to test.
* @pre AICargo::IsValidCargo(cargo_type).
* @return The road vehicle type needed to transport the cargo.
*/
static RoadVehicleType GetRoadVehicleTypeForCargo(CargoID cargo_type);
/**
* 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 IsRoadTypeAvailable(GetCurrentRoadType()).
* @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).
* @pre IsRoadTypeAvailable(GetCurrentRoadType()).
* @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).
* @pre IsRoadTypeAvailable(GetCurrentRoadType()).
* @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
* @note Construction will fail if an obstacle is found between the start and end tiles.
* @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
* @note Construction will fail if an obstacle is found between the start and end tiles.
* @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).
* @pre IsRoadTypeAvailable(GetCurrentRoadType()).
* @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
* @note Construction will fail if an obstacle is found between the start and end tiles.
* @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
* @note Construction will fail if an obstacle is found between the start and end tiles.
* @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.
* @pre IsRoadTypeAvailable(GetCurrentRoadType()).
* @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 station.
* @param front The tile exactly in front of the station.
* @param road_veh_type Whether to build a truck or bus station.
* @param station_id The station to join, AIStation::STATION_NEW or AIStation::STATION_JOIN_ADJACENT.
* @pre AIMap::IsValidTile(tile).
* @pre AIMap::IsValidTile(front).
* @pre 'tile' is not equal to 'front', but in a straight line of it.
* @pre station_id == AIStation::STATION_NEW || station_id == AIStation::STATION_JOIN_ADJACENT || AIStation::IsValidStation(station_id).
* @pre GetCurrentRoadType() == ROADTYPE_ROAD.
* @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, RoadVehicleType road_veh_type, StationID station_id);
/**
* Builds a drive-through road bus or truck station.
* @param tile Place to build the station.
* @param front A tile on the same axis with 'tile' as the station shall be oriented.
* @param road_veh_type Whether to build a truck or bus station.
* @param station_id The station to join, AIStation::STATION_NEW or AIStation::STATION_JOIN_ADJACENT.
* @pre AIMap::IsValidTile(tile).
* @pre AIMap::IsValidTile(front).
* @pre 'tile' is not equal to 'front', but in a straight line of it.
* @pre station_id == AIStation::STATION_NEW || station_id == AIStation::STATION_JOIN_ADJACENT || AIStation::IsValidStation(station_id).
* @pre IsRoadTypeAvailable(GetCurrentRoadType()).
* @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 BuildDriveThroughRoadStation(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, StationID station_id);
/**
* 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).
* @pre IsRoadTypeAvailable(GetCurrentRoadType()).
* @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).
* @pre IsRoadTypeAvailable(GetCurrentRoadType()).
* @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);
/**
* Get the baseprice of building a road-related object.
* @param roadtype the roadtype that is build (on)
* @param build_type the type of object to build
* @pre IsRoadTypeAvailable(railtype)
* @return The baseprice of building the given object.
*/
static Money GetBuildCost(RoadType roadtype, BuildType build_type);
private:
/**
* Internal function used by Build(OneWay)Road(Full).
*/
static bool _BuildRoadInternal(TileIndex start, TileIndex end, bool one_way, bool full);
/**
* Internal function used by Build(DriveThrough)RoadStation.
*/
static bool _BuildRoadStationInternal(TileIndex tile, TileIndex front, RoadVehicleType road_veh_type, bool drive_through, StationID station_id);
};
#endif /* SCRIPT_ROAD_HPP */

View File

@@ -0,0 +1,76 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_sign.cpp Implementation of AISign. */
#include "../../stdafx.h"
#include "script_sign.hpp"
#include "table/strings.h"
#include "../../ai/ai_instance.hpp"
#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 */ bool AISign::IsValidSign(SignID sign_id)
{
const Sign *si = ::Sign::GetIfValid(sign_id);
return si != NULL && si->owner == _current_company;
}
/* static */ bool AISign::SetName(SignID sign_id, const char *name)
{
EnforcePrecondition(false, IsValidSign(sign_id));
EnforcePrecondition(false, !::StrEmpty(name));
EnforcePreconditionCustomError(false, ::Utf8StringLength(name) < MAX_LENGTH_SIGN_NAME_CHARS, AIError::ERR_PRECONDITION_STRING_TOO_LONG);
return AIObject::DoCommand(0, sign_id, 0, CMD_RENAME_SIGN, name);
}
/* static */ 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 = ::Sign::Get(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, ::Utf8StringLength(text) < MAX_LENGTH_SIGN_NAME_CHARS, 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;
}

View File

@@ -0,0 +1,92 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_sign.hpp Everything to query and build signs. */
#ifndef SCRIPT_SIGN_HPP
#define SCRIPT_SIGN_HPP
#include "script_error.hpp"
/**
* Class that handles all sign related functions.
*/
class AISign : public AIObject {
public:
/**
* 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_ERROR_TOO_MANY_SIGNS]
};
/**
* 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 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 /* SCRIPT_SIGN_HPP */

View File

@@ -0,0 +1,23 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_signlist.cpp Implementation of AISignList and friends. */
#include "../../stdafx.h"
#include "script_signlist.hpp"
#include "script_sign.hpp"
#include "../../signs_base.h"
AISignList::AISignList()
{
Sign *s;
FOR_ALL_SIGNS(s) {
if (AISign::IsValidSign(s->index)) this->AddItem(s->index);
}
}

View File

@@ -0,0 +1,26 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_signlist.hpp List all the signs of your company. */
#ifndef SCRIPT_SIGNLIST_HPP
#define SCRIPT_SIGNLIST_HPP
#include "script_list.hpp"
/**
* Create a list of signs your company has created.
* @ingroup AIList
*/
class AISignList : public AIList {
public:
AISignList();
};
#endif /* SCRIPT_SIGNLIST_HPP */

View File

@@ -0,0 +1,120 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_station.cpp Implementation of AIStation. */
#include "../../stdafx.h"
#include "script_station.hpp"
#include "script_cargo.hpp"
#include "script_map.hpp"
#include "script_town.hpp"
#include "../../debug.h"
#include "../../station_base.h"
#include "../../roadstop_base.h"
#include "../../company_func.h"
#include "../../town.h"
/* static */ bool AIStation::IsValidStation(StationID station_id)
{
const Station *st = ::Station::GetIfValid(station_id);
return st != NULL && (st->owner == _current_company || st->owner == OWNER_NONE);
}
/* static */ StationID AIStation::GetStationID(TileIndex tile)
{
if (!::IsValidTile(tile) || !::IsTileType(tile, MP_STATION)) return INVALID_STATION;
return ::GetStationIndex(tile);
}
/* static */ int32 AIStation::GetCargoWaiting(StationID station_id, CargoID cargo_id)
{
if (!IsValidStation(station_id)) return -1;
if (!AICargo::IsValidCargo(cargo_id)) return -1;
return ::Station::Get(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 ::ToPercent8(::Station::Get(station_id)->goods[cargo_id].rating);
}
/* 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 (!HasExactlyOneBit(station_type)) 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 (!HasExactlyOneBit(station_type)) return false;
return (::Station::Get(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 = ::Station::Get(station_id)->GetPrimaryRoadStop(ROADSTOP_BUS); rs != NULL; rs = rs->next) {
if ((::GetRoadTypes(rs->xy) & r) != 0) return true;
}
for (const RoadStop *rs = ::Station::Get(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 ::Station::Get(station_id)->town->index;
}

View File

@@ -0,0 +1,158 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_station.hpp Everything to query and build stations. */
#ifndef SCRIPT_STATION_HPP
#define SCRIPT_STATION_HPP
#include "script_road.hpp"
#include "script_basestation.hpp"
/**
* Class that handles all station related functions.
*/
class AIStation : public AIBaseStation {
public:
/**
* 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 is build too close to another station, airport or dock */
ERR_STATION_TOO_CLOSE_TO_ANOTHER_STATION, // [STR_ERROR_TOO_CLOSE_TO_ANOTHER_AIRPORT, STR_ERROR_TOO_CLOSE_TO_ANOTHER_STATION, STR_ERROR_TOO_CLOSE_TO_ANOTHER_DOCK]
/** There are too many stations, airports and docks in the game */
ERR_STATION_TOO_MANY_STATIONS, // [STR_ERROR_TOO_MANY_STATIONS_LOADING, STR_ERROR_TOO_MANY_TRUCK_STOPS, STR_ERROR_TOO_MANY_BUS_STOPS]
/** There are too many stations, airports of docks in a town */
ERR_STATION_TOO_MANY_STATIONS_IN_TOWN, // [STR_ERROR_LOCAL_AUTHORITY_REFUSES_AIRPORT]
};
/**
* 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);
/**
* 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.
* The service quality of stations with signs within this radius
* influences 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 /* SCRIPT_STATION_HPP */

View File

@@ -0,0 +1,36 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_stationlist.cpp Implementation of AIStationList and friends. */
#include "../../stdafx.h"
#include "script_stationlist.hpp"
#include "script_vehicle.hpp"
#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 = ::Vehicle::Get(vehicle_id);
for (Order *o = v->GetFirstOrder(); o != NULL; o = o->next) {
if (o->IsType(OT_GOTO_STATION)) this->AddItem(o->GetDestination());
}
}

View File

@@ -0,0 +1,42 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_stationlist.hpp List all the stations (you own). */
#ifndef SCRIPT_STATIONLIST_HPP
#define SCRIPT_STATIONLIST_HPP
#include "script_list.hpp"
#include "script_station.hpp"
/**
* Creates a list of stations of which you are the owner.
* @ingroup AIList
*/
class AIStationList : public AIList {
public:
/**
* @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 AIList {
public:
/**
* @param vehicle_id The vehicle to get the list of stations he has in its orders from.
*/
AIStationList_Vehicle(VehicleID vehicle_id);
};
#endif /* SCRIPT_STATIONLIST_HPP */

View File

@@ -0,0 +1,85 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_subsidy.cpp Implementation of AISubsidy. */
#include "../../stdafx.h"
#include "script_subsidy.hpp"
#include "script_date.hpp"
#include "../../subsidy_base.h"
#include "../../station_base.h"
/* static */ bool AISubsidy::IsValidSubsidy(SubsidyID subsidy_id)
{
return ::Subsidy::IsValidID(subsidy_id);
}
/* static */ bool AISubsidy::IsAwarded(SubsidyID subsidy_id)
{
if (!IsValidSubsidy(subsidy_id)) return false;
return ::Subsidy::Get(subsidy_id)->IsAwarded();
}
/* static */ AICompany::CompanyID AISubsidy::GetAwardedTo(SubsidyID subsidy_id)
{
if (!IsAwarded(subsidy_id)) return AICompany::COMPANY_INVALID;
return (AICompany::CompanyID)((byte)::Subsidy::Get(subsidy_id)->awarded);
}
/* 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());
month += ::Subsidy::Get(subsidy_id)->remaining;
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 ::Subsidy::Get(subsidy_id)->cargo_type;
}
/* static */ AISubsidy::SubsidyParticipantType AISubsidy::GetSourceType(SubsidyID subsidy_id)
{
if (!IsValidSubsidy(subsidy_id)) return SPT_INVALID;
return (SubsidyParticipantType)(uint)::Subsidy::Get(subsidy_id)->src_type;
}
/* static */ int32 AISubsidy::GetSourceIndex(SubsidyID subsidy_id)
{
if (!IsValidSubsidy(subsidy_id)) return INVALID_STATION;
return ::Subsidy::Get(subsidy_id)->src;
}
/* static */ AISubsidy::SubsidyParticipantType AISubsidy::GetDestinationType(SubsidyID subsidy_id)
{
if (!IsValidSubsidy(subsidy_id)) return SPT_INVALID;
return (SubsidyParticipantType)(uint)::Subsidy::Get(subsidy_id)->dst_type;
}
/* static */ int32 AISubsidy::GetDestinationIndex(SubsidyID subsidy_id)
{
if (!IsValidSubsidy(subsidy_id)) return INVALID_STATION;
return ::Subsidy::Get(subsidy_id)->dst;
}

View File

@@ -0,0 +1,113 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_subsidy.hpp Everything to query subsidies. */
#ifndef SCRIPT_SUBSIDY_HPP
#define SCRIPT_SUBSIDY_HPP
#include "script_company.hpp"
/**
* Class that handles all subsidy related functions.
*/
class AISubsidy : public AIObject {
public:
/**
* Enumeration for source and destination of a subsidy.
* @note The list of values may grow in future.
*/
enum SubsidyParticipantType {
SPT_INDUSTRY = 0, ///< Subsidy participant is an industry
SPT_TOWN = 1, ///< Subsidy participant is a town
SPT_INVALID = 0xFF, ///< Invalid/unknown participant type
};
/**
* 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);
/**
* Returns the type of source of subsidy.
* @param subsidy_id The SubsidyID to check.
* @pre IsValidSubsidy(subsidy_id).
* @return Type of source of subsidy.
*/
static SubsidyParticipantType GetSourceType(SubsidyID subsidy_id);
/**
* Return the source IndustryID/TownID the subsidy is for.
* \li GetSourceType(subsidy_id) == SPT_INDUSTRY -> return the IndustryID.
* \li GetSourceType(subsidy_id) == SPT_TOWN -> return the TownID.
* @param subsidy_id The SubsidyID to check.
* @pre IsValidSubsidy(subsidy_id).
* @return One of TownID/IndustryID.
*/
static int32 GetSourceIndex(SubsidyID subsidy_id);
/**
* Returns the type of destination of subsidy.
* @param subsidy_id The SubsidyID to check.
* @pre IsValidSubsidy(subsidy_id).
* @return Type of destination of subsidy.
*/
static SubsidyParticipantType GetDestinationType(SubsidyID subsidy_id);
/**
* Return the destination IndustryID/TownID the subsidy is for.
* \li GetDestinationType(subsidy_id) == SPT_INDUSTRY -> return the IndustryID.
* \li GetDestinationType(subsidy_id) == SPT_TOWN -> return the TownID.
* @param subsidy_id the SubsidyID to check.
* @pre IsValidSubsidy(subsidy_id).
* @return One of TownID/IndustryID.
*/
static int32 GetDestinationIndex(SubsidyID subsidy_id);
};
#endif /* SCRIPT_SUBSIDY_HPP */

View File

@@ -0,0 +1,22 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_subsidylist.cpp Implementation of AISubsidyList. */
#include "../../stdafx.h"
#include "script_subsidylist.hpp"
#include "../../subsidy_base.h"
AISubsidyList::AISubsidyList()
{
const Subsidy *s;
FOR_ALL_SUBSIDIES(s) {
this->AddItem(s->index);
}
}

View File

@@ -0,0 +1,26 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_subsidylist.hpp List all the subsidies. */
#ifndef SCRIPT_SUBSIDYLIST_HPP
#define SCRIPT_SUBSIDYLIST_HPP
#include "script_list.hpp"
/**
* Creates a list of all current subsidies.
* @ingroup AIList
*/
class AISubsidyList : public AIList {
public:
AISubsidyList();
};
#endif /* SCRIPT_SUBSIDYLIST_HPP */

View File

@@ -0,0 +1,41 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_testmode.cpp Implementation of AITestMode. */
#include "../../stdafx.h"
#include "script_testmode.hpp"
#include "../../company_base.h"
#include "../../company_func.h"
#include "../../ai/ai_instance.hpp"
bool AITestMode::ModeProc()
{
/* 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()
{
if (this->GetDoCommandModeInstance() != this) {
/* Ignore this error if the AI already died. */
if (!AIObject::GetActiveInstance()->IsDead()) {
throw AI_FatalError("AITestmode object was removed while it was not the latest AI*Mode object created.");
}
}
this->SetDoCommandMode(this->last_mode, this->last_instance);
}

View File

@@ -0,0 +1,52 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_testmode.hpp Switch the AI to Test Mode. */
#ifndef SCRIPT_TESTMODE_HPP
#define SCRIPT_TESTMODE_HPP
#include "script_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 {
private:
AIModeProc *last_mode; ///< The previous mode we were in.
AIObject *last_instance; ///< The previous instace of the mode.
protected:
/**
* The callback proc for Testing mode.
*/
static bool ModeProc();
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 /* SCRIPT_TESTMODE_HPP */

View File

@@ -0,0 +1,298 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_tile.cpp Implementation of AITile. */
#include "../../stdafx.h"
#include "script_tile.hpp"
#include "script_map.hpp"
#include "script_town.hpp"
#include "script_cargo.hpp"
#include "../../station_func.h"
#include "../../company_func.h"
#include "../../water_map.h"
#include "../../clear_map.h"
#include "../../tree_map.h"
#include "../../town.h"
#include "../../landscape.h"
#include "../../economy_func.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 (!HasExactlyOneBit(::GetRoadBits(tile, ROADTYPE_ROAD))) 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)) ||
(::IsTileType(tile, MP_TREES) && ::GetTreeGround(tile) == TREE_GROUND_SHORE);
}
/* 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_ELEVATED | SLOPE_STEEP | SLOPE_HALFTILE_MASK)) != 0) return false;
return ::IsSteepSlope((::Slope)slope);
}
/* static */ bool AITile::IsHalftileSlope(Slope slope)
{
if ((slope & ~(SLOPE_ELEVATED | SLOPE_STEEP | SLOPE_HALFTILE_MASK)) != 0) return false;
return ::IsHalftileSlope((::Slope)slope);
}
/* static */ bool AITile::HasTreeOnTile(TileIndex tile)
{
if (!::IsValidTile(tile)) return false;
return ::IsTileType(tile, MP_TREES);
}
/* static */ bool AITile::IsFarmTile(TileIndex tile)
{
if (!::IsValidTile(tile)) return false;
return (::IsTileType(tile, MP_CLEAR) && ::IsClearGround(tile, CLEAR_FIELDS));
}
/* static */ bool AITile::IsRockTile(TileIndex tile)
{
if (!::IsValidTile(tile)) return false;
return (::IsTileType(tile, MP_CLEAR) && ::GetRawClearGround(tile) == ::CLEAR_ROCKS);
}
/* static */ bool AITile::IsRoughTile(TileIndex tile)
{
if (!::IsValidTile(tile)) return false;
return (::IsTileType(tile, MP_CLEAR) && ::GetRawClearGround(tile) == ::CLEAR_ROUGH);
}
/* static */ bool AITile::IsSnowTile(TileIndex tile)
{
if (!::IsValidTile(tile)) return false;
return (::IsTileType(tile, MP_CLEAR) && ::IsSnowTile(tile));
}
/* static */ bool AITile::IsDesertTile(TileIndex tile)
{
if (!::IsValidTile(tile)) return false;
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);
}
/* static */ AITile::Slope AITile::GetComplementSlope(Slope slope)
{
if ((slope & ~SLOPE_ELEVATED) != 0) return SLOPE_INVALID;
return (Slope)::ComplementSlope((::Slope)slope);
}
/* static */ int32 AITile::GetMinHeight(TileIndex tile)
{
if (!::IsValidTile(tile)) return -1;
return ::GetTileZ(tile);
}
/* static */ int32 AITile::GetMaxHeight(TileIndex tile)
{
if (!::IsValidTile(tile)) return -1;
return ::GetTileMaxZ(tile);
}
/* static */ int32 AITile::GetCornerHeight(TileIndex tile, Corner corner)
{
if (!::IsValidTile(tile) || !::IsValidCorner((::Corner)corner)) return -1;
int z;
::Slope slope = ::GetTileSlope(tile, &z);
return (z + ::GetSlopeZInCorner(slope, (::Corner)corner));
}
/* static */ AICompany::CompanyID AITile::GetOwner(TileIndex tile)
{
if (!::IsValidTile(tile)) return AICompany::COMPANY_INVALID;
if (::IsTileType(tile, MP_HOUSE)) return AICompany::COMPANY_INVALID;
if (::IsTileType(tile, MP_INDUSTRY)) return AICompany::COMPANY_INVALID;
return AICompany::ResolveCompanyID((AICompany::CompanyID)(byte)::GetTileOwner(tile));
}
/* static */ bool AITile::HasTransportType(TileIndex tile, TransportType transport_type)
{
if (!::IsValidTile(tile)) return false;
return ::TrackStatusToTrackdirBits(::GetTileTrackStatus(tile, (::TransportType)transport_type, UINT32_MAX)) != TRACKDIR_BIT_NONE;
}
/* static */ int32 AITile::GetCargoAcceptance(TileIndex tile, CargoID cargo_type, int width, int height, int radius)
{
if (!::IsValidTile(tile) || width <= 0 || height <= 0 || radius < 0 || !AICargo::IsValidCargo(cargo_type)) return -1;
CargoArray acceptance = ::GetAcceptanceAroundTiles(tile, width, height, _settings_game.station.modified_catchment ? radius : (int)CA_UNMODIFIED);
return acceptance[cargo_type];
}
/* static */ int32 AITile::GetCargoProduction(TileIndex tile, CargoID cargo_type, int width, int height, int radius)
{
if (!::IsValidTile(tile) || width <= 0 || height <= 0 || radius < 0 || !AICargo::IsValidCargo(cargo_type)) return -1;
CargoArray produced = ::GetProductionAroundTiles(tile, width, height, _settings_game.station.modified_catchment ? radius : (int)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, tile < ::MapSize());
return AIObject::DoCommand(tile, slope, 1, CMD_TERRAFORM_LAND);
}
/* static */ bool AITile::LowerTile(TileIndex tile, int32 slope)
{
EnforcePrecondition(false, tile < ::MapSize());
return AIObject::DoCommand(tile, slope, 0, CMD_TERRAFORM_LAND);
}
/* static */ bool AITile::LevelTiles(TileIndex start_tile, TileIndex end_tile)
{
EnforcePrecondition(false, start_tile < ::MapSize());
EnforcePrecondition(false, end_tile < ::MapSize());
return AIObject::DoCommand(end_tile, start_tile, LM_LEVEL << 1, CMD_LEVEL_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, TREE_INVALID, 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, TREE_INVALID, end_tile, CMD_PLANT_TREE);
}
/* static */ bool AITile::IsWithinTownInfluence(TileIndex tile, TownID town_id)
{
return AITown::IsWithinTownInfluence(town_id, tile);
}
/* static */ TownID AITile::GetTownAuthority(TileIndex tile)
{
if (!::IsValidTile(tile)) return false;
return ::ClosestTownFromTile(tile, _settings_game.economy.dist_local_authority)->index;
}
/* static */ TownID AITile::GetClosestTown(TileIndex tile)
{
if (!::IsValidTile(tile)) return INVALID_TOWN;
return ::ClosestTownFromTile(tile, UINT_MAX)->index;
}
/* static */ Money AITile::GetBuildCost(BuildType build_type)
{
switch (build_type) {
case BT_FOUNDATION: return ::GetPrice(PR_BUILD_FOUNDATION, 1, NULL);
case BT_TERRAFORM: return ::GetPrice(PR_TERRAFORM, 1, NULL);
case BT_BUILD_TREES: return ::GetPrice(PR_BUILD_TREES, 1, NULL);
case BT_CLEAR_GRASS: return ::GetPrice(PR_CLEAR_GRASS, 1, NULL);
case BT_CLEAR_ROUGH: return ::GetPrice(PR_CLEAR_ROUGH, 1, NULL);
case BT_CLEAR_ROCKY: return ::GetPrice(PR_CLEAR_ROCKS, 1, NULL);
case BT_CLEAR_FIELDS: return ::GetPrice(PR_CLEAR_FIELDS, 1, NULL);
case BT_CLEAR_HOUSE: return ::GetPrice(PR_CLEAR_HOUSE, 1, NULL);
default: return -1;
}
}

View File

@@ -0,0 +1,474 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_tile.hpp Everything to query and manipulate tiles. */
#ifndef SCRIPT_TILE_HPP
#define SCRIPT_TILE_HPP
#include "script_error.hpp"
#include "script_company.hpp"
/**
* Class that handles all tile related functions.
*/
class AITile : public AIObject {
public:
/**
* 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_ERROR_ALREADY_AT_SEA_LEVEL]
/** Tile can't be lowered any lower */
ERR_TILE_TOO_LOW, // [STR_ERROR_ALREADY_AT_SEA_LEVEL]
/** The area was already flat */
ERR_AREA_ALREADY_FLAT, // [STR_ERROR_ALREADY_LEVELLED]
/** There is a tunnel underneed */
ERR_EXCAVATION_WOULD_DAMAGE, // [STR_ERROR_EXCAVATION_WOULD_DAMAGE]
};
/**
* Enumeration for corners of tiles.
*/
enum Corner {
CORNER_W = 0, ///< West corner
CORNER_S = 1, ///< South corner
CORNER_E = 2, ///< East corner
CORNER_N = 3, ///< North corner
CORNER_INVALID = 0xFF,
};
/**
* Enumeration for the slope-type.
*
* 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 = 1 << CORNER_W, ///< The west corner of the tile is raised
SLOPE_S = 1 << CORNER_S, ///< The south corner of the tile is raised
SLOPE_E = 1 << CORNER_E, ///< The east corner of the tile is raised
SLOPE_N = 1 << CORNER_N, ///< The north corner of the tile is raised
SLOPE_STEEP = 0x10, ///< Indicates the slope is steep (The corner opposite of the not-raised corner is raised two times)
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, ///< Bit mask containing all 'simple' slopes. Does not appear as a slope.
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 = 0xFFFF, ///< 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.
TRANSPORT_INVALID = -1, ///< Tile without any transport type.
};
/**
* Get the base cost for building/clearing several things.
*/
enum BuildType {
BT_FOUNDATION, ///< Build a foundation under something
BT_TERRAFORM, ///< Terraform
BT_BUILD_TREES, ///< Build trees
BT_CLEAR_GRASS, ///< Clear a tile with just grass
BT_CLEAR_ROUGH, ///< Clear a rough tile
BT_CLEAR_ROCKY, ///< Clear a tile with rocks
BT_CLEAR_FIELDS, ///< Clear a tile with farm fields
BT_CLEAR_HOUSE, ///< Clear a tile with a house
};
/**
* 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. This is not
* true if there are also trees on the tile, see #HasTreeOnTile.
*/
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.
* Steep slopes are slopes with a height difference of 2 across one diagonal of the tile.
* @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.
* Halftile slopes appear on top of halftile foundations. E.g. the slope you get when building a horizontal railtrack on the top of a SLOPE_N or SLOPE_STEEP_N.
* @param slope The slope to check on.
* @pre slope != SLOPE_INVALID.
* @return True if the slope is a halftile slope.
* @note Currently there is no API function that would return or accept 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.
* This is the slope of the bare tile. A possible foundation on the tile does not influence this slope.
* @param tile The tile to check on.
* @pre AIMap::IsValidTile(tile).
* @return Bit mask encoding the slope. See #Slope for a description of the returned values.
*/
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 minimal height on a tile.
* The returned height is the height of the bare tile. A possible foundation on the tile does not influence this height.
* @param tile The tile to check on.
* @pre AIMap::IsValidTile(tile).
* @return The height of the lowest corner of the tile, ranging from 0 to 15.
*/
static int32 GetMinHeight(TileIndex tile);
/**
* Get the maximal height on a tile.
* The returned height is the height of the bare tile. A possible foundation on the tile does not influence this height.
* @param tile The tile to check on.
* @pre AIMap::IsValidTile(tile).
* @return The height of the highest corner of the tile, ranging from 0 to 15.
*/
static int32 GetMaxHeight(TileIndex tile);
/**
* Get the height of a certain corner of a tile.
* The returned height is the height of the bare tile. A possible foundation on the tile does not influence this height.
* @param tile The tile to check on.
* @param corner The corner to query.
* @pre AIMap::IsValidTile(tile).
* @return The height of the lowest corner of the tile, ranging from 0 to 15.
*/
static int32 GetCornerHeight(TileIndex tile, Corner corner);
/**
* 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 COMPANY_INVALID 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).
* @pre transport_type != TRANSPORT_AIR.
* @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.
* @note Use AIAirport.IsAirportTile to check for airport tiles. Aircraft
* can fly over every tile on the map so using HasTransportType
* doesn't make sense for TRANSPORT_AIR.
* @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).
* @pre AICargo::IsValidCargo(cargo_type)
* @pre width > 0.
* @pre height > 0.
* @pre radius >= 0.
* @return Value below 8 means no acceptance; the more the better.
*/
static int32 GetCargoAcceptance(TileIndex tile, CargoID cargo_type, int width, int height, int radius);
/**
* Checks how many producers in the radius produces this cargo.
* It creates a radius around the tile, and counts all producer of 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).
* @pre AICargo::IsValidCargo(cargo_type)
* @pre width > 0.
* @pre height > 0.
* @pre radius >= 0.
* @return The number of producers that produce this cargo within radius of the tile.
*/
static int32 GetCargoProduction(TileIndex tile, CargoID cargo_type, int width, int height, int 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) will raise the west and the north corner.
* @note The corners will be modified in the order west (first), south, east, north (last).
* Changing one corner might cause another corner to be changed too. So modifiing
* multiple corners may result in changing some corners by multiple steps.
* @param tile The tile to raise.
* @param slope Corners to raise (SLOPE_xxx).
* @pre tile < AIMap::GetMapSize().
* @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) will lower the west and the north corner.
* @note The corners will be modified in the order west (first), south, east, north (last).
* Changing one corner might cause another corner to be changed too. So modifiing
* multiple corners may result in changing some corners by multiple steps.
* @param tile The tile to lower.
* @param slope Corners to lower (SLOPE_xxx).
* @pre tile < AIMap::GetMapSize().
* @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);
/**
* Level all tiles in the rectangle between start_tile and end_tile so they
* are at the same height. All tiles will be raised or lowered until
* they are at height AITile::GetHeight(start_tile).
* @param start_tile One corner of the rectangle to level.
* @param end_tile The opposite corner of the rectangle.
* @pre start_tile < AIMap::GetMapSize().
* @pre end_tile < AIMap::GetMapSize().
* @exception AIError::ERR_AREA_NOT_CLEAR
* @exception AIError::ERR_TOO_CLOSE_TO_EDGE
* @return True if one or more tiles were leveled.
* @note Even if leveling some part fails, some other part may have been
* successfully leveled already.
* @note This function may return true in AITestMode, although it fails in
* AIExecMode.
*/
static bool LevelTiles(TileIndex start_tile, TileIndex end_tile);
/**
* 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.
* If a station sign would be on this tile, the servicing quality of the station would
* 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 which has authority for the tile.
* The rating of your company in this town will be checked and affected when
* building stations, trees etc.
* @param tile The tile to check.
* @return The TownID of the town which has authority on this tile.
*/
static TownID GetTownAuthority(TileIndex tile);
/**
* 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);
/**
* Get the baseprice of building/clearing various tile-related things.
* @param build_type the type to build
* @return The baseprice of building or removing the given object.
*/
static Money GetBuildCost(BuildType build_type);
};
#endif /* SCRIPT_TILE_HPP */

View File

@@ -0,0 +1,135 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_tilelist.cpp Implementation of AITileList and friends. */
#include "../../stdafx.h"
#include "script_tilelist.hpp"
#include "script_industry.hpp"
#include "../../industry.h"
#include "../../station_base.h"
void AITileList::AddRectangle(TileIndex t1, TileIndex t2)
{
if (!::IsValidTile(t1)) return;
if (!::IsValidTile(t2)) return;
TileArea ta(t1, t2);
TILE_AREA_LOOP(t, ta) this->AddItem(t);
}
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;
TileArea ta(t1, t2);
TILE_AREA_LOOP(t, ta) this->RemoveItem(t);
}
void AITileList::RemoveTile(TileIndex tile)
{
if (!::IsValidTile(tile)) return;
this->RemoveItem(tile);
}
AITileList_IndustryAccepting::AITileList_IndustryAccepting(IndustryID industry_id, int radius)
{
if (!AIIndustry::IsValidIndustry(industry_id) || radius <= 0) return;
const Industry *i = ::Industry::Get(industry_id);
/* Check if this industry accepts anything */
{
bool cargo_accepts = false;
for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
if (i->accepts_cargo[j] != CT_INVALID) cargo_accepts = true;
}
if (!cargo_accepts) return;
}
if (!_settings_game.station.modified_catchment) radius = CA_UNMODIFIED;
TileArea ta(i->location.tile - ::TileDiffXY(radius, radius), i->location.w + radius * 2, i->location.h + radius * 2);
TILE_AREA_LOOP(cur_tile, ta) {
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). */
CargoArray acceptance = ::GetAcceptanceAroundTiles(cur_tile, 1, 1, radius);
{
bool cargo_accepts = false;
for (byte j = 0; j < lengthof(i->accepts_cargo); j++) {
if (i->accepts_cargo[j] != CT_INVALID && acceptance[i->accepts_cargo[j]] != 0) cargo_accepts = true;
}
if (!cargo_accepts) continue;
}
this->AddTile(cur_tile);
}
}
AITileList_IndustryProducing::AITileList_IndustryProducing(IndustryID industry_id, int radius)
{
if (!AIIndustry::IsValidIndustry(industry_id) || radius <= 0) return;
const Industry *i = ::Industry::Get(industry_id);
/* Check if this industry produces anything */
bool cargo_produces = false;
for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
if (i->produced_cargo[j] != CT_INVALID) cargo_produces = true;
}
if (!cargo_produces) return;
if (!_settings_game.station.modified_catchment) radius = CA_UNMODIFIED;
TileArea ta(i->location.tile - ::TileDiffXY(radius, radius), i->location.w + radius * 2, i->location.h + radius * 2);
TILE_AREA_LOOP(cur_tile, ta) {
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;
this->AddTile(cur_tile);
}
}
AITileList_StationType::AITileList_StationType(StationID station_id, AIStation::StationType station_type)
{
if (!AIStation::IsValidStation(station_id)) return;
const StationRect *rect = &::Station::Get(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);
TileArea ta(::TileXY(rect->left, rect->top), rect->right - rect->left + 1, rect->bottom - rect->top + 1);
TILE_AREA_LOOP(cur_tile, ta) {
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);
}
}

View File

@@ -0,0 +1,103 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_tilelist.hpp List tiles. */
#ifndef SCRIPT_TILELIST_HPP
#define SCRIPT_TILELIST_HPP
#include "script_station.hpp"
#include "script_list.hpp"
/**
* Creates an empty list, in which you can add tiles.
* @ingroup AIList
*/
class AITileList : public AIList {
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:
/**
* @param industry_id The industry to create the AITileList around.
* @param radius The radius of the station you will be using.
* @pre AIIndustry::IsValidIndustry(industry_id).
* @pre radius > 0.
*/
AITileList_IndustryAccepting(IndustryID industry_id, int 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:
/**
* @param industry_id The industry to create the AITileList around.
* @param radius The radius of the station you will be using.
* @pre AIIndustry::IsValidIndustry(industry_id).
* @pre radius > 0.
*/
AITileList_IndustryProducing(IndustryID industry_id, int radius);
};
/**
* Creates a list of tiles which have the requested StationType of the
* StationID.
* @ingroup AIList
*/
class AITileList_StationType : public AITileList {
public:
/**
* @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 /* SCRIPT_TILELIST_HPP */

View File

@@ -0,0 +1,253 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_town.cpp Implementation of AITown. */
#include "../../stdafx.h"
#include "script_town.hpp"
#include "script_map.hpp"
#include "script_cargo.hpp"
#include "script_error.hpp"
#include "../../town.h"
#include "../../strings_func.h"
#include "../../company_func.h"
#include "../../station_base.h"
#include "../../landscape.h"
#include "table/strings.h"
/* static */ int32 AITown::GetTownCount()
{
return (int32)::Town::GetNumItems();
}
/* static */ bool AITown::IsValidTown(TownID town_id)
{
return ::Town::IsValidID(town_id);
}
/* static */ 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_NAME, &town_name[len - 1]);
return town_name;
}
/* static */ int32 AITown::GetPopulation(TownID town_id)
{
if (!IsValidTown(town_id)) return -1;
const Town *t = ::Town::Get(town_id);
return t->population;
}
/* static */ int32 AITown::GetHouseCount(TownID town_id)
{
if (!IsValidTown(town_id)) return -1;
const Town *t = ::Town::Get(town_id);
return t->num_houses;
}
/* static */ TileIndex AITown::GetLocation(TownID town_id)
{
if (!IsValidTown(town_id)) return INVALID_TILE;
const Town *t = ::Town::Get(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 = ::Town::Get(town_id);
return t->supplied[cargo_id].old_max;
}
/* static */ int32 AITown::GetLastMonthSupplied(TownID town_id, CargoID cargo_id)
{
if (!IsValidTown(town_id)) return -1;
if (!AICargo::IsValidCargo(cargo_id)) return -1;
const Town *t = ::Town::Get(town_id);
return t->supplied[cargo_id].old_act;
}
/* static */ int32 AITown::GetLastMonthTransportedPercentage(TownID town_id, CargoID cargo_id)
{
if (!IsValidTown(town_id)) return -1;
if (!AICargo::IsValidCargo(cargo_id)) return -1;
const Town *t = ::Town::Get(town_id);
return ::ToPercent8(t->GetPercentTransported(cargo_id));
}
/* static */ int32 AITown::GetLastMonthReceived(TownID town_id, AICargo::TownEffect towneffect_id)
{
if (!IsValidTown(town_id)) return -1;
if (!AICargo::IsValidTownEffect(towneffect_id)) return -1;
const Town *t = ::Town::Get(town_id);
return t->received[towneffect_id].old_act;
}
/* static */ uint32 AITown::GetCargoGoal(TownID town_id, AICargo::TownEffect towneffect_id)
{
if (!IsValidTown(town_id)) return -1;
if (!AICargo::IsValidTownEffect(towneffect_id)) return -1;
const Town *t = ::Town::Get(town_id);
switch (t->goal[towneffect_id]) {
case TOWN_GROWTH_WINTER:
if (TileHeight(t->xy) >= GetSnowLine() && t->population > 90) return 1;
return 0;
case TOWN_GROWTH_DESERT:
if (GetTropicZone(t->xy) == TROPICZONE_DESERT && t->population > 60) return 1;
return 0;
default: return t->goal[towneffect_id];
}
}
/* static */ int32 AITown::GetGrowthRate(TownID town_id)
{
if (!IsValidTown(town_id)) return false;
const Town *t = ::Town::Get(town_id);
return (t->growth_rate * TOWN_GROWTH_TICKS + DAY_TICKS) / DAY_TICKS;
}
/* 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 = ::Town::Get(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(::Town::Get(town_id)->statues, _current_company);
}
/* static */ bool AITown::IsCity(TownID town_id)
{
if (!IsValidTown(town_id)) return false;
return ::Town::Get(town_id)->larger_town;
}
/* static */ int AITown::GetRoadReworkDuration(TownID town_id)
{
if (!IsValidTown(town_id)) return -1;
return ::Town::Get(town_id)->road_build_months;
}
/* static */ AICompany::CompanyID AITown::GetExclusiveRightsCompany(TownID town_id)
{
if (!IsValidTown(town_id)) return AICompany::COMPANY_INVALID;
return (AICompany::CompanyID)(int8)::Town::Get(town_id)->exclusivity;
}
/* static */ int32 AITown::GetExclusiveRightsDuration(TownID town_id)
{
if (!IsValidTown(town_id)) return -1;
return ::Town::Get(town_id)->exclusive_counter;
}
/* static */ bool AITown::IsActionAvailable(TownID town_id, TownAction town_action)
{
if (!IsValidTown(town_id)) return false;
return HasBit(::GetMaskOfTownActions(NULL, _current_company, ::Town::Get(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(::Town::Get(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 TOWN_RATING_INVALID;
AICompany::CompanyID company = AICompany::ResolveCompanyID(company_id);
if (company == AICompany::COMPANY_INVALID) return TOWN_RATING_INVALID;
const Town *t = ::Town::Get(town_id);
if (!HasBit(t->have_ratings, company)) {
return TOWN_RATING_NONE;
} else if (t->ratings[company] <= RATING_APPALLING) {
return TOWN_RATING_APPALLING;
} else if (t->ratings[company] <= RATING_VERYPOOR) {
return TOWN_RATING_VERY_POOR;
} else if (t->ratings[company] <= RATING_POOR) {
return TOWN_RATING_POOR;
} else if (t->ratings[company] <= RATING_MEDIOCRE) {
return TOWN_RATING_MEDIOCRE;
} else if (t->ratings[company] <= RATING_GOOD) {
return TOWN_RATING_GOOD;
} else if (t->ratings[company] <= RATING_VERYGOOD) {
return TOWN_RATING_VERY_GOOD;
} else if (t->ratings[company] <= RATING_EXCELLENT) {
return TOWN_RATING_EXCELLENT;
} else {
return TOWN_RATING_OUTSTANDING;
}
}
/* static */ int AITown::GetAllowedNoise(TownID town_id)
{
if (!IsValidTown(town_id)) return -1;
const Town *t = ::Town::Get(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);
}
/* static */ AITown::RoadLayout AITown::GetRoadLayout(TownID town_id)
{
if (!IsValidTown(town_id)) return ROAD_LAYOUT_INVALID;
return (AITown::RoadLayout)((TownLayout)::Town::Get(town_id)->layout);
}

View File

@@ -0,0 +1,332 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_town.hpp Everything to query towns. */
#ifndef SCRIPT_TOWN_HPP
#define SCRIPT_TOWN_HPP
#include "script_cargo.hpp"
#include "script_company.hpp"
/**
* Class that handles all town related functions.
*/
class AITown : public AIObject {
public:
/**
* 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.
TOWN_RATING_INVALID = -1, ///< The town rating for invalid towns/companies.
};
/**
* Possible layouts for the roads in a town.
*/
enum RoadLayout {
/* Order IS important, as it matches an in-game value */
ROAD_LAYOUT_ORIGINAL, ///< Original algorithm (min. 1 distance between roads).
ROAD_LAYOUT_BETTER_ROADS, ///< Extended original algorithm (min. 2 distance between roads).
ROAD_LAYOUT_2x2, ///< Geometric 2x2 grid algorithm
ROAD_LAYOUT_3x3, ///< Geometric 3x3 grid algorithm
ROAD_LAYOUT_INVALID = -1, ///< The layout for invalid towns.
};
/**
* Gets the number of towns.
* @return The number of towns.
*/
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 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.
*/
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.
*/
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).
* @return The last month's production of the given cargo for this town.
*/
static int32 GetLastMonthProduction(TownID town_id, CargoID cargo_id);
/**
* Get the total amount of cargo supplied from a town last month.
* @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).
* @return The amount of cargo supplied for transport from this town last month.
*/
static int32 GetLastMonthSupplied(TownID town_id, CargoID cargo_id);
/**
* Get the percentage of transported 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).
* @return The percentage of given cargo transported from this town last month.
*/
static int32 GetLastMonthTransportedPercentage(TownID town_id, CargoID cargo_id);
/**
* Get the total amount of cargo effects received by a town last month.
* @param town_id The index of the town.
* @param towneffect_id The index of the cargo.
* @pre IsValidTown(town_id).
* @pre AICargo::IsValidTownEffect(cargo_id).
* @return The amount of cargo received by this town last month for this cargo effect.
*/
static int32 GetLastMonthReceived(TownID town_id, AICargo::TownEffect towneffect_id);
/**
* Get the amount of cargo that needs to be delivered (per TownEffect) for a
* town to grow. All goals need to be reached before a town will grow.
* @param town_id The index of the town.
* @param towneffect_id The index of the towneffect.
* @pre IsValidTown(town_id).
* @pre AICargo::IsValidTownEffect(cargo_id).
* @return The goal of the cargo.
* @note Goals can change over time. For example with a changing snowline, or
* with a growing town.
*/
static uint32 GetCargoGoal(TownID town_id, AICargo::TownEffect towneffect_id);
/**
* Get the amount of days between town growth.
* @param town_id The index of the town.
* @pre IsValidTown(town_id).
* @return True if the action succeeded.
* @note This function does not indicate when it will grow next. It only tells you the time between growths.
*/
static int32 GetGrowthRate(TownID town_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.
* If a station sign would be on this tile, the servicing quality of the station would
* 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 if the town is a city.
* @param town_id The town to check.
* @pre IsValidTown(town_id).
* @return True if the town is a city.
*/
static bool IsCity(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::COMPANY_INVALID 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::COMPANY_INVALID.
* @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);
/**
* Get the road layout for a town.
* @param town_id The town to get the road layout from.
* @return The RoadLayout for the town.
*/
static RoadLayout GetRoadLayout(TownID town_id);
};
#endif /* SCRIPT_TOWN_HPP */

View File

@@ -0,0 +1,29 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_townlist.cpp Implementation of AITownList and friends. */
#include "../../stdafx.h"
#include "script_townlist.hpp"
#include "../../town.h"
AITownList::AITownList()
{
Town *t;
FOR_ALL_TOWNS(t) {
this->AddItem(t->index);
}
}
AITownEffectList::AITownEffectList()
{
for (int i = TE_BEGIN; i < TE_END; i++) {
this->AddItem(i);
}
}

View File

@@ -0,0 +1,35 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_townlist.hpp List all the towns. */
#ifndef SCRIPT_TOWNLIST_HPP
#define SCRIPT_TOWNLIST_HPP
#include "script_list.hpp"
/**
* Creates a list of towns that are currently on the map.
* @ingroup AIList
*/
class AITownList : public AIList {
public:
AITownList();
};
/**
* Creates a list of all TownEffects known in the game.
* @ingroup AIList
*/
class AITownEffectList : public AIList {
public:
AITownEffectList();
};
#endif /* SCRIPT_TOWNLIST_HPP */

View File

@@ -0,0 +1,134 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_tunnel.cpp Implementation of AITunnel. */
#include "../../stdafx.h"
#include "script_tunnel.hpp"
#include "script_rail.hpp"
#include "../../ai/ai_instance.hpp"
#include "../../tunnel_map.h"
#include "../../command_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 already, take the easy way out! */
if (IsTunnelTile(tile)) return ::GetOtherTunnelEnd(tile);
int start_z;
Slope start_tileh = ::GetTileSlope(tile, &start_z);
DiagDirection direction = ::GetInclinedSlopeDirection(start_tileh);
if (direction == INVALID_DIAGDIR) return INVALID_TILE;
TileIndexDiff delta = ::TileOffsByDiagDir(direction);
int end_z;
do {
tile += delta;
if (!::IsValidTile(tile)) return INVALID_TILE;
::GetTileSlope(tile, &end_z);
} while (start_z != end_z);
return tile;
}
/**
* Helper function to connect a just built tunnel to nearby roads.
* @param instance The AI we have to built the road for.
*/
static void _DoCommandReturnBuildTunnel2(class AIInstance *instance)
{
if (!AITunnel::_BuildTunnelRoad2()) {
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();
}
/**
* Helper function to connect a just built tunnel to nearby roads.
* @param instance The AI we have to built the road for.
*/
static void _DoCommandReturnBuildTunnel1(class AIInstance *instance)
{
if (!AITunnel::_BuildTunnelRoad1()) {
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::VT_RAIL || vehicle_type == AIVehicle::VT_ROAD);
EnforcePrecondition(false, vehicle_type != AIVehicle::VT_RAIL || AIRail::IsRailTypeAvailable(AIRail::GetCurrentRailType()));
uint type = 0;
if (vehicle_type == AIVehicle::VT_ROAD) {
type |= (TRANSPORT_ROAD << 8);
type |= ::RoadTypeToRoadTypes((::RoadType)AIObject::GetRoadType());
} else {
type |= (TRANSPORT_RAIL << 8);
type |= AIRail::GetCurrentRailType();
}
/* For rail we do nothing special */
if (vehicle_type == AIVehicle::VT_RAIL) {
return AIObject::DoCommand(start, type, 0, CMD_BUILD_TUNNEL);
}
AIObject::SetCallbackVariable(0, start);
return AIObject::DoCommand(start, type, 0, CMD_BUILD_TUNNEL, NULL, &::_DoCommandReturnBuildTunnel1);
}
/* 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 = ::DiagdirBetweenTiles(end, start);
DiagDirection dir_2 = ::ReverseDiagDir(dir_1);
return AIObject::DoCommand(start + ::TileOffsByDiagDir(dir_1), ::DiagDirToRoadBits(dir_2) | (AIObject::GetRoadType() << 4), 0, CMD_BUILD_ROAD, NULL, &::_DoCommandReturnBuildTunnel2);
}
/* 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 = ::DiagdirBetweenTiles(end, start);
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);
}

View File

@@ -0,0 +1,111 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_tunnel.hpp Everything to query and build tunnels. */
#ifndef SCRIPT_TUNNEL_HPP
#define SCRIPT_TUNNEL_HPP
#include "script_vehicle.hpp"
/**
* Class that handles all tunnel related functions.
*/
class AITunnel : public AIObject {
public:
/**
* 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_ERROR_CAN_T_BUILD_ON_WATER]
/** The start tile must slope either North, South, West or East */
ERR_TUNNEL_START_SITE_UNSUITABLE, // [STR_ERROR_SITE_UNSUITABLE_FOR_TUNNEL]
/** Another tunnel is in the way */
ERR_TUNNEL_ANOTHER_TUNNEL_IN_THE_WAY, // [STR_ERROR_ANOTHER_TUNNEL_IN_THE_WAY]
/** Unable to excavate land at the end to create the tunnel's exit */
ERR_TUNNEL_END_SITE_UNSUITABLE, // [STR_ERROR_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. If there is no 'simple' inclined slope at the start tile,
* this function will return AIMap::TILE_INVALID.
* @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
* AIMap::TILE_INVALID if no other end was found (can't build tunnel).
* @note Even if this function returns a valid tile, that is no guarantee
* that building a tunnel will succeed. Use BuildTunnel in AITestMode to
* check whether a tunnel can actually be build.
*/
static TileIndex GetOtherTunnelEnd(TileIndex tile);
#ifndef DOXYGEN_AI_DOCS
/**
* 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 /* DOXYGEN_AI_DOCS */
/**
* 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::VT_ROAD || (vehicle_type == AIVehicle::VT_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 /* SCRIPT_TUNNEL_HPP */

View File

@@ -0,0 +1,106 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/**
* @file script_types.hpp Defines all the types of the game, like IDs of various objects.
*
* IDs are used to identify certain objects. They are only unique within the object type, so for example a vehicle may have VehicleID 2009,
* while a station has StationID 2009 at the same time. Also IDs are assigned arbitrary, you cannot assume them to be consecutive.
* Also note that some IDs are static and never change, while others are allocated dynamically and might be
* reused for other objects once they are released. So be careful, which IDs you store for which purpose and whether they stay valid all the time.
*
* <table>
* <tr><th>type </th><th> object </th>
* <th> acquired </th>
* <th> released </th>
* <th> reused </th></tr>
* <tr><td>#BridgeID </td><td> bridge type </td>
* <td> introduction \ref newgrf_changes "(1)" </td>
* <td> never \ref newgrf_changes "(1)" </td>
* <td> no \ref newgrf_changes "(1)" </td></tr>
* <tr><td>#CargoID </td><td> cargo type </td>
* <td> game start \ref newgrf_changes "(1)" </td>
* <td> never \ref newgrf_changes "(1)" </td>
* <td> no \ref newgrf_changes "(1)" </td></tr>
* <tr><td>#EngineID </td><td> engine type </td>
* <td> introduction, preview \ref dynamic_engines "(2)" </td>
* <td> engines retires \ref dynamic_engines "(2)" </td>
* <td> no \ref dynamic_engines "(2)" </td></tr>
* <tr><td>#GroupID </td><td> vehicle group </td>
* <td> creation </td>
* <td> deletion </td>
* <td> yes </td></tr>
* <tr><td>#IndustryID </td><td> industry </td>
* <td> construction </td>
* <td> closure </td>
* <td> yes </td></tr>
* <tr><td>#IndustryType</td><td> industry type </td>
* <td> game start \ref newgrf_changes "(1)" </td>
* <td> never \ref newgrf_changes "(1)" </td>
* <td> no </td></tr>
* <tr><td>#SignID </td><td> sign </td>
* <td> construction </td>
* <td> deletion </td>
* <td> yes </td></tr>
* <tr><td>#StationID </td><td> station </td>
* <td> construction </td>
* <td> expiration of 'grey' station sign after deletion </td>
* <td> yes </td></tr>
* <tr><td>#SubsidyID </td><td> subsidy </td>
* <td> offer announcement </td>
* <td> (offer) expiration </td>
* <td> yes </td></tr>
* <tr><td>#TileIndex </td><td> tile on map </td>
* <td> game start </td>
* <td> never </td>
* <td> no </td></tr>
* <tr><td>#TownID </td><td> town </td>
* <td> game start </td>
* <td> never </td>
* <td> no </td></tr>
* <tr><td>#VehicleID </td><td> vehicle </td>
* <td> construction, autorenew, autoreplace </td>
* <td> destruction, autorenew, autoreplace </td>
* <td> yes </td></tr>
* </table>
*
* @remarks
* \li \anchor newgrf_changes (1) in-game changes of newgrfs may reassign/invalidate IDs (will also cause other trouble though).
* \li \anchor dynamic_engines (2) engine IDs are reassigned/invalidated on changing 'allow multiple newgrf engine sets' (only allowed as long as no vehicles are built).
*/
#ifndef SCRIPT_TYPES_HPP
#define SCRIPT_TYPES_HPP
#include "../../core/overflowsafe_type.hpp"
#include "../../company_type.h"
#include <squirrel.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 uint16 SubsidyID; ///< The ID of a subsidy.
typedef uint32 TileIndex; ///< The ID of a tile (just named differently).
typedef uint16 TownID; ///< The ID of a town.
typedef uint32 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.
#endif /* SCRIPT_TYPES_HPP */

View File

@@ -0,0 +1,423 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_vehicle.cpp Implementation of AIVehicle. */
#include "../../stdafx.h"
#include "script_engine.hpp"
#include "script_cargo.hpp"
#include "script_gamesettings.hpp"
#include "script_group.hpp"
#include "../../ai/ai_instance.hpp"
#include "../../company_func.h"
#include "../../string_func.h"
#include "../../strings_func.h"
#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)
{
const Vehicle *v = ::Vehicle::GetIfValid(vehicle_id);
return v != NULL && v->owner == _current_company && (v->IsPrimaryVehicle() || (v->type == VEH_TRAIN && ::Train::From(v)->IsFreeWagon()));
}
/* static */ int32 AIVehicle::GetNumWagons(VehicleID vehicle_id)
{
if (!IsValidVehicle(vehicle_id)) return -1;
int num = 1;
const Train *v = ::Train::GetIfValid(vehicle_id);
if (v != NULL) {
while ((v = v->GetNextUnit()) != NULL) num++;
}
return num;
}
/* static */ int AIVehicle::GetLength(VehicleID vehicle_id)
{
if (!IsValidVehicle(vehicle_id)) return -1;
const Vehicle *v = ::Vehicle::Get(vehicle_id);
return v->IsGroundVehicle() ? v->GetGroundVehicleCache()->cached_total_length : -1;
}
/* static */ VehicleID AIVehicle::BuildVehicle(TileIndex depot, EngineID engine_id)
{
EnforcePrecondition(VEHICLE_INVALID, AIEngine::IsBuildable(engine_id));
::VehicleType type = ::Engine::Get(engine_id)->type;
EnforcePreconditionCustomError(VEHICLE_INVALID, !AIGameSettings::IsDisabledVehicleType((AIVehicle::VehicleType)type), AIVehicle::ERR_VEHICLE_BUILD_DISABLED);
if (!AIObject::DoCommand(depot, engine_id, 0, ::GetCmdBuildVeh(type), NULL, &AIInstance::DoCommandReturnVehicleID)) return VEHICLE_INVALID;
/* 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 VEHICLE_INVALID;
/* In case of test-mode, we return VehicleID 0 */
return 0;
}
/* static */ bool AIVehicle::_MoveWagonInternal(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, ::Vehicle::Get(source_vehicle_id)->type == VEH_TRAIN);
EnforcePrecondition(false, dest_vehicle_id == -1 || ::Vehicle::Get(dest_vehicle_id)->type == VEH_TRAIN);
const Train *v = ::Train::Get(source_vehicle_id);
while (source_wagon-- > 0) v = v->GetNextUnit();
const Train *w = NULL;
if (dest_vehicle_id != -1) {
w = ::Train::Get(dest_vehicle_id);
while (dest_wagon-- > 0) w = w->GetNextUnit();
}
return AIObject::DoCommand(0, v->index | (move_attached_wagons ? 1 : 0) << 20, w == NULL ? ::INVALID_VEHICLE : w->index, CMD_MOVE_RAIL_VEHICLE);
}
/* static */ bool AIVehicle::MoveWagon(VehicleID source_vehicle_id, int source_wagon, int dest_vehicle_id, int dest_wagon)
{
return _MoveWagonInternal(source_vehicle_id, source_wagon, false, dest_vehicle_id, dest_wagon);
}
/* static */ bool AIVehicle::MoveWagonChain(VehicleID source_vehicle_id, int source_wagon, int dest_vehicle_id, int dest_wagon)
{
return _MoveWagonInternal(source_vehicle_id, source_wagon, true, dest_vehicle_id, dest_wagon);
}
/* 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(::Vehicle::Get(vehicle_id)));
return res.Succeeded() ? _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(::Vehicle::Get(vehicle_id)));
}
/* static */ bool AIVehicle::SellVehicle(VehicleID vehicle_id)
{
EnforcePrecondition(false, IsValidVehicle(vehicle_id));
const Vehicle *v = ::Vehicle::Get(vehicle_id);
return AIObject::DoCommand(0, vehicle_id | (v->type == VEH_TRAIN ? 1 : 0) << 20, 0, GetCmdSellVeh(v));
}
/* static */ bool AIVehicle::_SellWagonInternal(VehicleID vehicle_id, int wagon, bool sell_attached_wagons)
{
EnforcePrecondition(false, IsValidVehicle(vehicle_id) && wagon < GetNumWagons(vehicle_id));
EnforcePrecondition(false, ::Vehicle::Get(vehicle_id)->type == VEH_TRAIN);
const Train *v = ::Train::Get(vehicle_id);
while (wagon-- > 0) v = v->GetNextUnit();
return AIObject::DoCommand(0, v->index | (sell_attached_wagons ? 1 : 0) << 20, 0, CMD_SELL_VEHICLE);
}
/* static */ bool AIVehicle::SellWagon(VehicleID vehicle_id, int wagon)
{
return _SellWagonInternal(vehicle_id, wagon, false);
}
/* static */ bool AIVehicle::SellWagonChain(VehicleID vehicle_id, int wagon)
{
return _SellWagonInternal(vehicle_id, wagon, true);
}
/* static */ bool AIVehicle::SendVehicleToDepot(VehicleID vehicle_id)
{
EnforcePrecondition(false, IsValidVehicle(vehicle_id));
return AIObject::DoCommand(0, vehicle_id, 0, GetCmdSendToDepot(::Vehicle::Get(vehicle_id)));
}
/* static */ bool AIVehicle::SendVehicleToDepotForServicing(VehicleID vehicle_id)
{
EnforcePrecondition(false, IsValidVehicle(vehicle_id));
return AIObject::DoCommand(0, vehicle_id | DEPOT_SERVICE, 0, GetCmdSendToDepot(::Vehicle::Get(vehicle_id)));
}
/* static */ bool AIVehicle::IsInDepot(VehicleID vehicle_id)
{
if (!IsValidVehicle(vehicle_id)) return false;
return ::Vehicle::Get(vehicle_id)->IsInDepot();
}
/* static */ bool AIVehicle::IsStoppedInDepot(VehicleID vehicle_id)
{
if (!IsValidVehicle(vehicle_id)) return false;
return ::Vehicle::Get(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::ReverseVehicle(VehicleID vehicle_id)
{
EnforcePrecondition(false, IsValidVehicle(vehicle_id));
EnforcePrecondition(false, ::Vehicle::Get(vehicle_id)->type == VEH_ROAD || ::Vehicle::Get(vehicle_id)->type == VEH_TRAIN);
switch (::Vehicle::Get(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, ::Utf8StringLength(name) < MAX_LENGTH_VEHICLE_NAME_CHARS, 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 = ::Vehicle::Get(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 ::Vehicle::Get(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 = ::Vehicle::Get(vehicle_id);
if (v->type == VEH_TRAIN) {
while (wagon-- > 0) v = ::Train::From(v)->GetNextUnit();
}
return v->engine_type;
}
/* static */ int32 AIVehicle::GetUnitNumber(VehicleID vehicle_id)
{
if (!IsValidVehicle(vehicle_id)) return -1;
return ::Vehicle::Get(vehicle_id)->unitnumber;
}
/* static */ 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 ::Vehicle::Get(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 = ::Vehicle::Get(vehicle_id);
if (v->type == VEH_TRAIN) {
while (wagon-- > 0) v = ::Train::From(v)->GetNextUnit();
}
return v->age;
}
/* static */ int32 AIVehicle::GetMaxAge(VehicleID vehicle_id)
{
if (!IsValidVehicle(vehicle_id)) return -1;
return ::Vehicle::Get(vehicle_id)->max_age;
}
/* static */ int32 AIVehicle::GetAgeLeft(VehicleID vehicle_id)
{
if (!IsValidVehicle(vehicle_id)) return -1;
return ::Vehicle::Get(vehicle_id)->max_age - ::Vehicle::Get(vehicle_id)->age;
}
/* static */ int32 AIVehicle::GetCurrentSpeed(VehicleID vehicle_id)
{
if (!IsValidVehicle(vehicle_id)) return -1;
return ::Vehicle::Get(vehicle_id)->GetDisplaySpeed(); // km-ish/h
}
/* static */ AIVehicle::VehicleState AIVehicle::GetState(VehicleID vehicle_id)
{
if (!IsValidVehicle(vehicle_id)) return AIVehicle::VS_INVALID;
const Vehicle *v = ::Vehicle::Get(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 ::Vehicle::Get(vehicle_id)->GetRunningCost() >> 8;
}
/* static */ Money AIVehicle::GetProfitThisYear(VehicleID vehicle_id)
{
if (!IsValidVehicle(vehicle_id)) return -1;
return ::Vehicle::Get(vehicle_id)->GetDisplayProfitThisYear();
}
/* static */ Money AIVehicle::GetProfitLastYear(VehicleID vehicle_id)
{
if (!IsValidVehicle(vehicle_id)) return -1;
return ::Vehicle::Get(vehicle_id)->GetDisplayProfitLastYear();
}
/* static */ Money AIVehicle::GetCurrentValue(VehicleID vehicle_id)
{
if (!IsValidVehicle(vehicle_id)) return -1;
return ::Vehicle::Get(vehicle_id)->value;
}
/* static */ AIVehicle::VehicleType AIVehicle::GetVehicleType(VehicleID vehicle_id)
{
if (!IsValidVehicle(vehicle_id)) return VT_INVALID;
switch (::Vehicle::Get(vehicle_id)->type) {
case VEH_ROAD: return VT_ROAD;
case VEH_TRAIN: return VT_RAIL;
case VEH_SHIP: return VT_WATER;
case VEH_AIRCRAFT: return VT_AIR;
default: return VT_INVALID;
}
}
/* static */ AIRoad::RoadType AIVehicle::GetRoadType(VehicleID vehicle_id)
{
if (!IsValidVehicle(vehicle_id)) return AIRoad::ROADTYPE_INVALID;
if (GetVehicleType(vehicle_id) != VT_ROAD) return AIRoad::ROADTYPE_INVALID;
return (AIRoad::RoadType)(::RoadVehicle::Get(vehicle_id))->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 = ::Vehicle::Get(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 = ::Vehicle::Get(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 AIGroup::GROUP_INVALID;
return ::Vehicle::Get(vehicle_id)->group_id;
}
/* static */ bool AIVehicle::IsArticulated(VehicleID vehicle_id)
{
if (!IsValidVehicle(vehicle_id)) return false;
if (GetVehicleType(vehicle_id) != VT_ROAD && GetVehicleType(vehicle_id) != VT_RAIL) return false;
const Vehicle *v = ::Vehicle::Get(vehicle_id);
switch (v->type) {
case VEH_ROAD: return ::RoadVehicle::From(v)->HasArticulatedPart();
case VEH_TRAIN: return ::Train::From(v)->HasArticulatedPart();
default: NOT_REACHED();
}
}
/* static */ bool AIVehicle::HasSharedOrders(VehicleID vehicle_id)
{
if (!IsValidVehicle(vehicle_id)) return false;
Vehicle *v = ::Vehicle::Get(vehicle_id);
return v->orders.list != NULL && v->orders.list->GetNumVehicles() > 1;
}
/* static */ int AIVehicle::GetReliability(VehicleID vehicle_id)
{
if (!IsValidVehicle(vehicle_id)) return -1;
const Vehicle *v = ::Vehicle::Get(vehicle_id);
return ::ToPercent16(v->reliability);
}

View File

@@ -0,0 +1,541 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_vehicle.hpp Everything to query and build vehicles. */
#ifndef SCRIPT_VEHICLE_HPP
#define SCRIPT_VEHICLE_HPP
#include "script_road.hpp"
/**
* Class that handles all vehicle related functions.
*/
class AIVehicle : public AIObject {
public:
/**
* 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_ERROR_TOO_MANY_VEHICLES_IN_GAME]
/** Vehicle is not available */
ERR_VEHICLE_NOT_AVAILABLE, // [STR_ERROR_AIRCRAFT_NOT_AVAILABLE, STR_ERROR_ROAD_VEHICLE_NOT_AVAILABLE, STR_ERROR_SHIP_NOT_AVAILABLE, STR_ERROR_RAIL_VEHICLE_NOT_AVAILABLE]
/** Vehicle can't be build due to game settigns */
ERR_VEHICLE_BUILD_DISABLED, // [STR_ERROR_CAN_T_BUY_TRAIN, STR_ERROR_CAN_T_BUY_ROAD_VEHICLE, STR_ERROR_CAN_T_BUY_SHIP, STR_ERROR_CAN_T_BUY_AIRCRAFT]
/** Vehicle can't be build in the selected depot */
ERR_VEHICLE_WRONG_DEPOT, // [STR_ERROR_DEPOT_WRONG_DEPOT_TYPE]
/** Vehicle can't return to the depot */
ERR_VEHICLE_CANNOT_SEND_TO_DEPOT, // [STR_ERROR_CAN_T_SEND_TRAIN_TO_DEPOT, STR_ERROR_CAN_T_SEND_ROAD_VEHICLE_TO_DEPOT, STR_ERROR_CAN_T_SEND_SHIP_TO_DEPOT, STR_ERROR_CAN_T_SEND_AIRCRAFT_TO_HANGAR]
/** Vehicle can't start / stop */
ERR_VEHICLE_CANNOT_START_STOP, // [STR_ERROR_CAN_T_STOP_START_TRAIN, STR_ERROR_CAN_T_STOP_START_ROAD_VEHICLE, STR_ERROR_CAN_T_STOP_START_SHIP, STR_ERROR_CAN_T_STOP_START_AIRCRAFT]
/** Vehicle can't turn */
ERR_VEHICLE_CANNOT_TURN, // [STR_ERROR_CAN_T_MAKE_ROAD_VEHICLE_TURN, STR_ERROR_CAN_T_REVERSE_DIRECTION_TRAIN, STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE, STR_ERROR_CAN_T_REVERSE_DIRECTION_RAIL_VEHICLE_MULTIPLE_UNITS]
/** Vehicle can't be refit */
ERR_VEHICLE_CANNOT_REFIT, // [STR_ERROR_CAN_T_REFIT_TRAIN, STR_ERROR_CAN_T_REFIT_ROAD_VEHICLE, STR_ERROR_CAN_T_REFIT_SHIP, STR_ERROR_CAN_T_REFIT_AIRCRAFT]
/** Vehicle is destroyed */
ERR_VEHICLE_IS_DESTROYED, // [STR_ERROR_VEHICLE_IS_DESTROYED]
/** Vehicle is not in a depot */
ERR_VEHICLE_NOT_IN_DEPOT, // [STR_ERROR_AIRCRAFT_MUST_BE_STOPPED_INSIDE_HANGAR, STR_ERROR_ROAD_VEHICLE_MUST_BE_STOPPED_INSIDE_DEPOT, STR_ERROR_TRAIN_MUST_BE_STOPPED_INSIDE_DEPOT, STR_ERROR_SHIP_MUST_BE_STOPPED_INSIDE_DEPOT]
/** Vehicle is flying */
ERR_VEHICLE_IN_FLIGHT, // [STR_ERROR_AIRCRAFT_IS_IN_FLIGHT]
/** Vehicle is without power */
ERR_VEHICLE_NO_POWER, // [STR_ERROR_TRAIN_START_NO_POWER]
/** Vehicle would get too long during construction. */
ERR_VEHICLE_TOO_LONG, // [STR_ERROR_TRAIN_TOO_LONG]
};
/**
* 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 */
VT_RAIL, ///< Rail type vehicle.
VT_ROAD, ///< Road type vehicle (bus / truck).
VT_WATER, ///< Water type vehicle.
VT_AIR, ///< Air type vehicle.
VT_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.
};
static const VehicleID VEHICLE_INVALID = 0xFFFFF; ///< Invalid VehicleID.
/**
* 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 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 The speed is in OpenTTD's internal speed unit.
* This is mph / 1.6, which is roughly km/h.
* To get km/h multiply this number by 1.00584.
*/
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 365 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) == VT_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 AIEngine::IsBuildable(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 its 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 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) == VT_RAIL.
* @pre dest_vehicle_id == -1 || GetVehicleType(dest_vehicle_id) == VT_RAIL.
* @return Whether or not moving the wagon succeeded.
*/
static bool MoveWagon(VehicleID source_vehicle_id, int source_wagon, int dest_vehicle_id, int dest_wagon);
/**
* Move a chain of wagons after another wagon.
* @param source_vehicle_id The vehicle to move a wagon away from.
* @param source_wagon The first wagon in source_vehicle to move.
* @param dest_vehicle_id The vehicle to move the wagons to, or -1 to create a new vehicle.
* @param dest_wagon The wagon in dest_vehicle to place source_wagon and following wagons 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) == VT_RAIL.
* @pre dest_vehicle_id == -1 || GetVehicleType(dest_vehicle_id) == VT_RAIL.
* @return Whether or not moving the wagons succeeded.
*/
static bool MoveWagonChain(VehicleID source_vehicle_id, int source_wagon, 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.
* @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 has been sold.
*/
static bool SellWagon(VehicleID vehicle_id, int wagon);
/**
* Sells all wagons from the vehicle starting from a given position.
* @param vehicle_id The vehicle to sell a wagon from.
* @param wagon The wagon 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 wagons have been sold.
*/
static bool SellWagonChain(VehicleID vehicle_id, int wagon);
/**
* Sends the given vehicle to a depot. If the vehicle has already been
* sent to a depot it continues with its normal orders instead.
* @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 the current order was changed.
*/
static bool SendVehicleToDepot(VehicleID vehicle_id);
/**
* Sends the given vehicle to a depot for servicing. If the vehicle has
* already been sent to a depot it continues with its normal orders instead.
* @param vehicle_id The vehicle to send to a depot for servicing.
* @pre IsValidVehicle(vehicle_id).
* @exception AIVehicle::ERR_VEHICLE_CANNOT_SEND_TO_DEPOT
* @return True if the current order was changed.
*/
static bool SendVehicleToDepotForServicing(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);
/**
* 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) == VT_ROAD || GetVehicleType(vehicle_id) == VT_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) == VT_ROAD || GetVehicleType(vehicle_id) == VT_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) == VT_ROAD || GetVehicleType(vehicle_id) == VT_RAIL.
* @return True if the vehicle is articulated.
*/
static bool IsArticulated(VehicleID vehicle_id);
/**
* Check if the vehicle has shared orders.
* @param vehicle_id The vehicle to check.
* @pre IsValidVehicle(vehicle_id).
* @return True if the vehicle has shared orders.
*/
static bool HasSharedOrders(VehicleID vehicle_id);
/**
* Get the current reliability of a vehicle.
* @param vehicle_id The vehicle to check.
* @pre IsValidVehicle(vehicle_id).
* @return The current reliability (0-100%).
*/
static int GetReliability(VehicleID vehicle_id);
private:
/**
* Internal function used by SellWagon(Chain).
*/
static bool _SellWagonInternal(VehicleID vehicle_id, int wagon, bool sell_attached_wagons);
/**
* Internal function used by MoveWagon(Chain).
*/
static bool _MoveWagonInternal(VehicleID source_vehicle_id, int source_wagon, bool move_attached_wagons, int dest_vehicle_id, int dest_wagon);
};
#endif /* SCRIPT_VEHICLE_HPP */

View File

@@ -0,0 +1,130 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_vehiclelist.cpp Implementation of AIVehicleList and friends. */
#include "../../stdafx.h"
#include "script_vehiclelist.hpp"
#include "script_group.hpp"
#include "script_map.hpp"
#include "script_station.hpp"
#include "../../company_func.h"
#include "../../depot_map.h"
#include "../../vehicle_base.h"
AIVehicleList::AIVehicleList()
{
const 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 (!AIBaseStation::IsValidBaseStation(station_id)) return;
const 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->IsType(OT_GOTO_WAYPOINT)) && order->GetDestination() == station_id) {
this->AddItem(v->index);
break;
}
}
}
}
}
AIVehicleList_Depot::AIVehicleList_Depot(TileIndex tile)
{
if (!AIMap::IsValidTile(tile)) return;
DestinationID dest;
VehicleType type;
switch (GetTileType(tile)) {
case MP_STATION: // Aircraft
if (!IsAirport(tile)) return;
type = VEH_AIRCRAFT;
dest = GetStationIndex(tile);
break;
case MP_RAILWAY:
if (!IsRailDepot(tile)) return;
type = VEH_TRAIN;
dest = GetDepotIndex(tile);
break;
case MP_ROAD:
if (!IsRoadDepot(tile)) return;
type = VEH_ROAD;
dest = GetDepotIndex(tile);
break;
case MP_WATER:
if (!IsShipDepot(tile)) return;
type = VEH_SHIP;
dest = GetDepotIndex(tile);
break;
default: // No depot
return;
}
const Vehicle *v;
FOR_ALL_VEHICLES(v) {
if (v->owner == _current_company && v->IsPrimaryVehicle() && v->type == type) {
const Order *order;
FOR_VEHICLE_ORDERS(v, order) {
if (order->IsType(OT_GOTO_DEPOT) && order->GetDestination() == dest) {
this->AddItem(v->index);
break;
}
}
}
}
}
AIVehicleList_SharedOrders::AIVehicleList_SharedOrders(VehicleID vehicle_id)
{
if (!AIVehicle::IsValidVehicle(vehicle_id)) return;
for (const Vehicle *v = Vehicle::Get(vehicle_id)->FirstShared(); v != NULL; v = v->NextShared()) {
this->AddItem(v->index);
}
}
AIVehicleList_Group::AIVehicleList_Group(GroupID group_id)
{
if (!AIGroup::IsValidGroup((AIGroup::GroupID)group_id)) return;
const Vehicle *v;
FOR_ALL_VEHICLES(v) {
if (v->owner == _current_company && v->IsPrimaryVehicle()) {
if (v->group_id == group_id) this->AddItem(v->index);
}
}
}
AIVehicleList_DefaultGroup::AIVehicleList_DefaultGroup(AIVehicle::VehicleType vehicle_type)
{
if (vehicle_type < AIVehicle::VT_RAIL || vehicle_type > AIVehicle::VT_AIR) return;
const Vehicle *v;
FOR_ALL_VEHICLES(v) {
if (v->owner == _current_company && v->IsPrimaryVehicle()) {
if (v->type == vehicle_type && v->group_id == AIGroup::GROUP_DEFAULT) this->AddItem(v->index);
}
}
}

View File

@@ -0,0 +1,92 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_vehiclelist.hpp List all the vehicles (you own). */
#ifndef SCRIPT_VEHICLELIST_HPP
#define SCRIPT_VEHICLELIST_HPP
#include "script_list.hpp"
#include "script_vehicle.hpp"
/**
* Creates a list of vehicles of which you are the owner.
* @ingroup AIList
*/
class AIVehicleList : public AIList {
public:
AIVehicleList();
};
/**
* Creates a list of vehicles that have orders to a given station.
* @ingroup AIList
*/
class AIVehicleList_Station : public AIList {
public:
/**
* @param station_id The station to get the list of vehicles from, which have orders to it.
* @pre AIBaseStation::IsValidBaseStation(station_id)
*/
AIVehicleList_Station(StationID station_id);
};
/**
* Creates a list of vehicles that have orders to a given depot.
* The list is created with a tile. If the tile is part of an airport all
* aircraft having a depot order on a hangar of that airport will be
* returned. For all other vehicle types the tile has to be a depot or
* an empty list will be returned.
* @ingroup AIList
*/
class AIVehicleList_Depot : public AIList {
public:
/**
* @param tile The tile of the depot to get the list of vehicles from, which have orders to it.
*/
AIVehicleList_Depot(TileIndex tile);
};
/**
* Creates a list of vehicles that share orders.
* @ingroup AIList
*/
class AIVehicleList_SharedOrders : public AIList {
public:
/**
* @param vehicle_id The vehicle that the rest shared orders with.
*/
AIVehicleList_SharedOrders(VehicleID vehicle_id);
};
/**
* Creates a list of vehicles that are in a group.
* @ingroup AIList
*/
class AIVehicleList_Group : public AIList {
public:
/**
* @param group_id The ID of the group the vehicles are in.
*/
AIVehicleList_Group(GroupID group_id);
};
/**
* Creates a list of vehicles that are in the default group.
* @ingroup AIList
*/
class AIVehicleList_DefaultGroup : public AIList {
public:
/**
* @param vehicle_type The VehicleType to get the list of vehicles for.
*/
AIVehicleList_DefaultGroup(AIVehicle::VehicleType vehicle_type);
};
#endif /* SCRIPT_VEHICLELIST_HPP */

View File

@@ -0,0 +1,38 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_waypoint.cpp Implementation of AIWaypoint. */
#include "../../stdafx.h"
#include "script_waypoint.hpp"
#include "script_rail.hpp"
#include "script_marine.hpp"
#include "../../company_func.h"
#include "../../waypoint_base.h"
/* static */ bool AIWaypoint::IsValidWaypoint(StationID waypoint_id)
{
const Waypoint *wp = ::Waypoint::GetIfValid(waypoint_id);
return wp != NULL && (wp->owner == _current_company || wp->owner == OWNER_NONE);
}
/* static */ StationID AIWaypoint::GetWaypointID(TileIndex tile)
{
if (!AIRail::IsRailWaypointTile(tile) && !AIMarine::IsBuoyTile(tile)) return STATION_INVALID;
return ::GetStationIndex(tile);
}
/* static */ bool AIWaypoint::HasWaypointType(StationID waypoint_id, WaypointType waypoint_type)
{
if (!IsValidWaypoint(waypoint_id)) return false;
if (!HasExactlyOneBit(waypoint_type)) return false;
return (::Waypoint::Get(waypoint_id)->facilities & waypoint_type) != 0;
}

View File

@@ -0,0 +1,72 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_waypoint.hpp Everything to query and build waypoints. */
#ifndef SCRIPT_WAYPOINT_HPP
#define SCRIPT_WAYPOINT_HPP
#include "script_basestation.hpp"
/**
* Class that handles all waypoint related functions.
*/
class AIWaypoint : public AIBaseStation {
public:
/**
* Type of waypoints known in the game.
*/
enum WaypointType {
/* Values are important, as they represent the internal state of the game. */
WAYPOINT_RAIL = 0x01, ///< Rail waypoint
WAYPOINT_BUOY = 0x10, ///< Buoy
WAYPOINT_ANY = 0x11, ///< All waypoint types
};
/**
* All waypoint related error messages.
*/
enum ErrorMessages {
/** Base for waypoint related errors */
ERR_WAYPOINT_BASE = AIError::ERR_CAT_WAYPOINT << AIError::ERR_CAT_BIT_SIZE,
/** The waypoint is build too close to another waypoint */
ERR_WAYPOINT_TOO_CLOSE_TO_ANOTHER_WAYPOINT, // [STR_ERROR_TOO_CLOSE_TO_ANOTHER_WAYPOINT]
/** The waypoint would join more than one existing waypoint together. */
ERR_WAYPOINT_ADJOINS_MULTIPLE_WAYPOINTS, // [STR_ERROR_WAYPOINT_ADJOINS_MORE_THAN_ONE_EXISTING]
};
/**
* Checks whether the given waypoint is valid and owned by you.
* @param waypoint_id The waypoint to check.
* @return True if and only if the waypoint is valid.
*/
static bool IsValidWaypoint(StationID waypoint_id);
/**
* Get the StationID of a tile.
* @param tile The tile to find the StationID of.
* @pre AIRail::IsRailWaypointTile(tile).
* @return StationID of the waypoint.
*/
static StationID GetWaypointID(TileIndex tile);
/**
* Check if any part of the waypoint contains a waypoint of the type waypoint_type
* @param waypoint_id The waypoint to look at.
* @param waypoint_type The WaypointType to look for.
* @return True if the waypoint has a waypoint part of the type waypoint_type.
*/
static bool HasWaypointType(StationID waypoint_id, WaypointType waypoint_type);
};
DECLARE_ENUM_AS_BIT_SET(AIWaypoint::WaypointType)
#endif /* SCRIPT_WAYPOINT_HPP */

View File

@@ -0,0 +1,37 @@
/* $Id$ */
/*
* This file is part of OpenTTD.
* OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
* OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
*/
/** @file script_waypointlist.cpp Implementation of AIWaypointList and friends. */
#include "../../stdafx.h"
#include "script_waypointlist.hpp"
#include "script_vehicle.hpp"
#include "../../company_func.h"
#include "../../vehicle_base.h"
#include "../../waypoint_base.h"
AIWaypointList::AIWaypointList(AIWaypoint::WaypointType waypoint_type)
{
const Waypoint *wp;
FOR_ALL_WAYPOINTS(wp) {
if ((wp->facilities & waypoint_type) &&
(wp->owner == _current_company || wp->owner == OWNER_NONE)) this->AddItem(wp->index);
}
}
AIWaypointList_Vehicle::AIWaypointList_Vehicle(VehicleID vehicle_id)
{
if (!AIVehicle::IsValidVehicle(vehicle_id)) return;
const Vehicle *v = ::Vehicle::Get(vehicle_id);
for (const Order *o = v->GetFirstOrder(); o != NULL; o = o->next) {
if (o->IsType(OT_GOTO_WAYPOINT)) this->AddItem(o->GetDestination());
}
}

Some files were not shown because too many files have changed in this diff Show More