1
0
Fork 0

(svn r16121) [0.7] -Backport from trunk:

- Add: [NoAI] UseAsRandomAI as function in info.nut. When an AI returns false, it will never be chosen as random AI (r16113)
- Fix: [NoAI] Make sure AIOrder::GetDestination always returns a tile belonging to the station (16109)
- Fix: [NoAI] When giving an aircraft a goto-hangar order do not let it be a normal goto-station order (r16108)
- Add: [NoAI] AIOF_STOP_IN_DEPOT to the orderflags in AIOrder to allow stop-in-depot orders (r16107)
- Fix: [NoAI] AIOrder::SetOrderFlags always removed "Service if needed" from goto-depot orders (r16106)
- Add: [NoAI] GetURL() as possible function to info.nut. If AIs implement it, that url is shown when the AI crashes and also in the AI selection window [FS#2808] (r16093)
- Change: [NoAI] Reverse the order of the lines in the AI debug window [FS#2778] (r16091)
- Fix: [NoAI] AIDebug window profiled the blitters by invalidating itself unconditionally on repaint. OTOH it was not invalidated in other cases when needed (r16094)
- Fix: [NoAI] MOF_COND_DESTINATION was not accepted by CmdModifyOrder() (r16063)
release/0.7
rubidium 2009-04-22 09:00:19 +00:00
parent f7cc97fe5c
commit 28e7981a5b
13 changed files with 167 additions and 22 deletions

View File

@ -103,6 +103,11 @@ struct AIListWindow : public Window {
sprintf(buf, "%d", selected_info->GetVersion());
DoDrawStringTruncated(buf, x + 5, y, TC_BLACK, this->width - x - 8);
y += 13;
if (selected_info->GetURL() != NULL) {
SetDParamStr(0, selected_info->GetURL());
DrawString(4, y, STR_AI_URL, TC_BLACK);
y += 13;
}
SetDParamStr(0, selected_info->GetDescription());
DrawStringMultiLine(4, y, STR_JUST_RAW_STRING, this->width - 8, this->widget[AIL_WIDGET_INFO_BG].bottom - y);
}
@ -575,6 +580,8 @@ struct AIDebugWindow : public Window {
static CompanyID ai_debug_company;
int redraw_timer;
int last_vscroll_pos;
bool autoscroll;
AIDebugWindow(const WindowDesc *desc, WindowNumber number) : Window(desc, number)
{
@ -587,6 +594,8 @@ struct AIDebugWindow : public Window {
this->vscroll.cap = 14;
this->vscroll.pos = 0;
this->resize.step_height = 12;
this->last_vscroll_pos = 0;
this->autoscroll = true;
if (ai_debug_company != INVALID_COMPANY) this->LowerWidget(ai_debug_company + AID_WIDGET_COMPANY_BUTTON_START);
@ -664,13 +673,35 @@ struct AIDebugWindow : public Window {
AILog::LogData *log = (AILog::LogData *)AIObject::GetLogPointer();
_current_company = old_company;
SetVScrollCount(this, (log == NULL) ? 0 : log->used);
int scroll_count = (log == NULL) ? 0 : log->used;
if (this->vscroll.count != scroll_count) {
SetVScrollCount(this, scroll_count);
/* We need a repaint */
this->InvalidateWidget(AID_WIDGET_SCROLLBAR);
}
if (log == NULL) return;
/* Detect when the user scrolls the window. Enable autoscroll when the
* bottom-most line becomes visible. */
if (this->last_vscroll_pos != this->vscroll.pos) {
this->autoscroll = this->vscroll.pos >= log->used - this->vscroll.cap;
}
if (this->autoscroll) {
int scroll_pos = max(0, log->used - this->vscroll.cap);
if (scroll_pos != this->vscroll.pos) {
this->vscroll.pos = scroll_pos;
/* We need a repaint */
this->InvalidateWidget(AID_WIDGET_SCROLLBAR);
}
}
last_vscroll_pos = this->vscroll.pos;
int y = 6;
for (int i = this->vscroll.pos; i < (this->vscroll.cap + this->vscroll.pos); i++) {
uint pos = (log->count + log->pos - i) % log->count;
for (int i = this->vscroll.pos; i < (this->vscroll.cap + this->vscroll.pos) && i < log->used; i++) {
uint pos = (i + log->pos + 1 - log->used + log->count) % log->count;
if (log->lines[pos] == NULL) break;
TextColour colour;
@ -693,6 +724,8 @@ struct AIDebugWindow : public Window {
this->RaiseWidget(ai_debug_company + AID_WIDGET_COMPANY_BUTTON_START);
ai_debug_company = show_ai;
this->LowerWidget(ai_debug_company + AID_WIDGET_COMPANY_BUTTON_START);
this->autoscroll = true;
this->last_vscroll_pos = this->vscroll.pos;
this->SetDirty();
}
@ -726,6 +759,7 @@ struct AIDebugWindow : public Window {
virtual void OnResize(Point new_size, Point delta)
{
this->vscroll.cap += delta.y / (int)this->resize.step_height;
SetVScrollCount(this, this->vscroll.count); // vscroll.pos should be in a valid range
}
};

View File

@ -36,6 +36,7 @@ AIFileInfo::~AIFileInfo()
free((void *)this->description);
free((void *)this->date);
free((void *)this->instance_name);
free((void *)this->url);
free(this->main_script);
free(this->SQ_instance);
}
@ -98,6 +99,11 @@ bool AIFileInfo::CheckMethod(const char *name) const
if (!info->engine->CallIntegerMethod(*info->SQ_instance, "GetVersion", &info->version)) return SQ_ERROR;
if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "CreateInstance", &info->instance_name)) return SQ_ERROR;
/* The GetURL function is optional. */
if (info->engine->MethodExists(*info->SQ_instance, "GetURL")) {
if (!info->engine->CallStringMethodStrdup(*info->SQ_instance, "GetURL", &info->url)) return SQ_ERROR;
}
return 0;
}
@ -125,6 +131,12 @@ bool AIFileInfo::CheckMethod(const char *name) const
} else {
info->min_loadable_version = info->GetVersion();
}
/* When there is an UseAsRandomAI function, call it. */
if (info->engine->MethodExists(*info->SQ_instance, "UseAsRandomAI")) {
if (!info->engine->CallBoolMethod(*info->SQ_instance, "UseAsRandomAI", &info->use_as_random)) return SQ_ERROR;
} else {
info->use_as_random = true;
}
/* Remove the link to the real instance, else it might get deleted by RegisterAI() */
sq_setinstanceup(vm, 2, NULL);

View File

@ -41,7 +41,7 @@ public:
friend class AIInfo;
friend class AILibrary;
AIFileInfo() : SQ_instance(NULL), main_script(NULL), author(NULL), name(NULL), short_name(NULL), description(NULL), date(NULL), instance_name(NULL) {};
AIFileInfo() : SQ_instance(NULL), main_script(NULL), author(NULL), name(NULL), short_name(NULL), description(NULL), date(NULL), instance_name(NULL), url(NULL) {};
~AIFileInfo();
/**
@ -84,6 +84,11 @@ public:
*/
const char *GetInstanceName() const { return this->instance_name; }
/**
* Get the website for this script.
*/
const char *GetURL() const { return this->url; }
/**
* Get the filename of the main.nut script.
*/
@ -111,6 +116,7 @@ private:
const char *date;
const char *instance_name;
int version;
const char *url;
};
class AIInfo : public AIFileInfo {
@ -155,9 +161,15 @@ public:
*/
int GetSettingDefaultValue(const char *name) const;
/**
* Use this AI as a random AI.
*/
bool UseAsRandomAI() const { return this->use_as_random; }
private:
AIConfigItemList config_list;
int min_loadable_version;
bool use_as_random;
};
class AILibrary : public AIFileInfo {

View File

@ -19,6 +19,7 @@
#define DEFINE_SCRIPT_FILES
#include "ai_info.hpp"
#include "ai_config.hpp"
#include "ai_storage.hpp"
#include "ai_instance.hpp"
#include "ai_gui.hpp"
@ -258,6 +259,12 @@ void AIInstance::Died()
if (strcmp(GetCompany(_current_company)->ai_info->GetMainScript(), "%_dummy") != 0) {
ShowErrorMessage(INVALID_STRING_ID, STR_AI_PLEASE_REPORT_CRASH, 0, 0);
}
const AIInfo *info = AIConfig::GetConfig(_current_company)->GetInfo();
if (info->GetURL() != NULL) {
AILog::Info("Please report the error to the following URL:");
AILog::Info(info->GetURL());
}
}
void AIInstance::GameLoop()

View File

@ -345,20 +345,31 @@ void AIScanner::RegisterAI(AIInfo *info)
AIInfo *AIScanner::SelectRandomAI()
{
if (this->info_single_list.size() == 0) {
uint num_random_ais = 0;
for (AIInfoList::iterator it = this->info_single_list.begin(); it != this->info_single_list.end(); it++) {
if (it->second->UseAsRandomAI()) num_random_ais++;
}
if (num_random_ais == 0) {
DEBUG(ai, 0, "No suitable AI found, loading 'dummy' AI.");
return this->info_dummy;
}
/* Find a random AI */
uint pos;
if (_networking) pos = InteractiveRandomRange((uint16)this->info_single_list.size());
else pos = RandomRange((uint16)this->info_single_list.size());
if (_networking) {
pos = InteractiveRandomRange(num_random_ais);
} else {
pos = RandomRange(num_random_ais);
}
/* Find the Nth item from the array */
AIInfoList::iterator it = this->info_single_list.begin();
for (; pos > 0; pos--) it++;
AIInfoList::iterator first_it = it;
while (!it->second->UseAsRandomAI()) it++;
for (; pos > 0; pos--) {
it++;
while (!it->second->UseAsRandomAI()) it++;
}
return (*it).second;
}

View File

@ -32,7 +32,7 @@
log->lines = CallocT<char *>(80);
log->type = CallocT<AILog::AILogType>(80);
log->count = 80;
log->pos = log->count;
log->pos = log->count - 1;
log->used = 0;
}
LogData *log = (LogData *)AIObject::GetLogPointer();

View File

@ -23,7 +23,10 @@ static OrderType GetOrderTypeByTile(TileIndex t)
switch (::GetTileType(t)) {
default: break;
case MP_STATION: return OT_GOTO_STATION; break;
case MP_STATION:
if (IsHangar(t)) return OT_GOTO_DEPOT;
return OT_GOTO_STATION;
break;
case MP_WATER: if (::IsShipDepot(t)) return OT_GOTO_DEPOT; break;
case MP_ROAD: if (::GetRoadTileType(t) == ROAD_TILE_DEPOT) return OT_GOTO_DEPOT; break;
case MP_RAILWAY:
@ -74,7 +77,10 @@ static OrderType GetOrderTypeByTile(TileIndex t)
(((order_flags & AIOF_NO_UNLOAD) == 0) || ((order_flags & AIOF_NO_LOAD) == 0)) &&
(((order_flags & AIOF_FULL_LOAD_ANY) == 0) || ((order_flags & AIOF_NO_LOAD) == 0));
case OT_GOTO_DEPOT: return (order_flags & ~(AIOF_NON_STOP_FLAGS | AIOF_SERVICE_IF_NEEDED)) == 0;
case OT_GOTO_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;
}
@ -118,11 +124,36 @@ static OrderType GetOrderTypeByTile(TileIndex t)
}
switch (order->GetType()) {
case OT_GOTO_DEPOT:
case OT_GOTO_DEPOT: {
if (v->type != VEH_AIRCRAFT) return ::GetDepot(order->GetDestination())->xy;
/* FALL THROUGH: aircraft's hangars are referenced by StationID, not DepotID */
/* Aircraft's hangars are referenced by StationID, not DepotID */
const Station *st = ::GetStation(order->GetDestination());
const AirportFTAClass *airport = st->Airport();
if (airport == NULL || airport->nof_depots == 0) return INVALID_TILE;
return st->airport_tile + ::ToTileIndexDiff(st->Airport()->airport_depots[0]);
}
case OT_GOTO_STATION: return ::GetStation(order->GetDestination())->xy;
case OT_GOTO_STATION: {
const Station *st = ::GetStation(order->GetDestination());
if (st->train_tile != INVALID_TILE) {
for (uint i = 0; i < st->trainst_w; i++) {
TileIndex t = st->train_tile + TileDiffXY(i, 0);
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) {
const AirportFTAClass *fta = st->Airport();
BEGIN_TILE_LOOP(tile, fta->size_x, fta->size_y, st->airport_tile) {
if (!::IsHangar(tile)) return tile;
} END_TILE_LOOP(tile, fta->size_x, fta->size_y, st->airport_tile)
}
return INVALID_TILE;
}
case OT_GOTO_WAYPOINT: return ::GetWaypoint(order->GetDestination())->xy;
default: return INVALID_TILE;
}
@ -145,6 +176,7 @@ static OrderType GetOrderTypeByTile(TileIndex t)
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;
break;
case OT_GOTO_STATION:
@ -260,9 +292,20 @@ static OrderType GetOrderTypeByTile(TileIndex t)
Order order;
switch (::GetOrderTypeByTile(destination)) {
case OT_GOTO_DEPOT:
order.MakeGoToDepot(::GetDepotByTile(destination)->index, (OrderDepotTypeFlags)(ODTFB_PART_OF_ORDERS | ((order_flags & AIOF_SERVICE_IF_NEEDED) ? ODTFB_SERVICE : 0)));
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));
/* Check explicitly if the order is to a station (for aircraft) or
* to a depot (other vehicle types). */
if (::GetVehicle(vehicle_id)->type == VEH_AIRCRAFT) {
if (!::IsTileType(destination, MP_STATION)) return false;
order.MakeGoToDepot(::GetStationIndex(destination), odtf, odaf);
} else {
if (::IsTileType(destination, MP_STATION)) return false;
order.MakeGoToDepot(::GetDepotByTile(destination)->index, odtf, odaf);
}
break;
}
case OT_GOTO_STATION:
order.MakeGoToStation(::GetStationIndex(destination));
@ -356,8 +399,11 @@ static void _DoCommandReturnSetOrderFlags(class AIInstance *instance)
switch (order->GetType()) {
case OT_GOTO_DEPOT:
if ((current & AIOF_SERVICE_IF_NEEDED) != (order_flags & AIOF_SERVICE_IF_NEEDED)) {
return AIObject::DoCommand(0, vehicle_id | (order_position << 16), MOF_DEPOT_ACTION, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnSetOrderFlags);
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_position << 16), (data << 4) | MOF_DEPOT_ACTION, CMD_MODIFY_ORDER, NULL, &_DoCommandReturnSetOrderFlags);
}
break;

View File

@ -57,6 +57,8 @@ public:
/** 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,
/** All flags related to non-stop settings. */
AIOF_NON_STOP_FLAGS = AIOF_NON_STOP_INTERMEDIATE | AIOF_NON_STOP_DESTINATION,
@ -64,6 +66,8 @@ public:
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,
/** For marking invalid order flags */
AIOF_INVALID = 0xFFFF,

View File

@ -42,9 +42,11 @@ void SQAIOrder_Register(Squirrel *engine) {
SQAIOrder.DefSQConst(engine, AIOrder::AIOF_FULL_LOAD_ANY, "AIOF_FULL_LOAD_ANY");
SQAIOrder.DefSQConst(engine, AIOrder::AIOF_NO_LOAD, "AIOF_NO_LOAD");
SQAIOrder.DefSQConst(engine, AIOrder::AIOF_SERVICE_IF_NEEDED, "AIOF_SERVICE_IF_NEEDED");
SQAIOrder.DefSQConst(engine, AIOrder::AIOF_STOP_IN_DEPOT, "AIOF_STOP_IN_DEPOT");
SQAIOrder.DefSQConst(engine, AIOrder::AIOF_NON_STOP_FLAGS, "AIOF_NON_STOP_FLAGS");
SQAIOrder.DefSQConst(engine, AIOrder::AIOF_UNLOAD_FLAGS, "AIOF_UNLOAD_FLAGS");
SQAIOrder.DefSQConst(engine, AIOrder::AIOF_LOAD_FLAGS, "AIOF_LOAD_FLAGS");
SQAIOrder.DefSQConst(engine, AIOrder::AIOF_DEPOT_FLAGS, "AIOF_DEPOT_FLAGS");
SQAIOrder.DefSQConst(engine, AIOrder::AIOF_INVALID, "AIOF_INVALID");
SQAIOrder.DefSQConst(engine, AIOrder::OC_LOAD_PERCENTAGE, "OC_LOAD_PERCENTAGE");
SQAIOrder.DefSQConst(engine, AIOrder::OC_RELIABILITY, "OC_RELIABILITY");

View File

@ -3585,6 +3585,7 @@ STR_AI_RANDOM_AI :Random AI
STR_AI_SETTINGS_CAPTION :{WHITE}AI Parameters
STR_AI_AUTHOR :Author:
STR_AI_VERSION :Version:
STR_AI_URL :URL: {RAW_STRING}
STR_AI_PLEASE_REPORT_CRASH :{WHITE}One of the running AIs crashed. Please report this to the AI author with a screenshot of the AI Debug Window.
########

View File

@ -498,7 +498,8 @@ CommandCost CmdInsertOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
if (new_order.GetNonStopType() != ONSF_STOP_EVERYWHERE && v->type != VEH_TRAIN && v->type != VEH_ROAD) return CMD_ERROR;
if (new_order.GetDepotOrderType() & ~(ODTFB_PART_OF_ORDERS | ((new_order.GetDepotOrderType() & ODTFB_PART_OF_ORDERS) != 0 ? ODTFB_SERVICE : 0))) return CMD_ERROR;
if (new_order.GetDepotActionType() & ~ODATFB_NEAREST_DEPOT) return CMD_ERROR;
if (new_order.GetDepotActionType() & ~(ODATFB_HALT | ODATFB_NEAREST_DEPOT)) return CMD_ERROR;
if ((new_order.GetDepotOrderType() & ODTFB_SERVICE) && (new_order.GetDepotActionType() & ODATFB_HALT)) return CMD_ERROR;
break;
}
@ -862,7 +863,7 @@ CommandCost CmdModifyOrder(TileIndex tile, DoCommandFlag flags, uint32 p1, uint3
break;
case OT_CONDITIONAL:
if (mof != MOF_COND_VARIABLE && mof != MOF_COND_COMPARATOR && mof != MOF_COND_VALUE) return CMD_ERROR;
if (mof != MOF_COND_VARIABLE && mof != MOF_COND_COMPARATOR && mof != MOF_COND_VALUE && mof != MOF_COND_DESTINATION) return CMD_ERROR;
break;
default:

View File

@ -234,6 +234,15 @@ bool Squirrel::CallIntegerMethod(HSQOBJECT instance, const char *method_name, in
return true;
}
bool Squirrel::CallBoolMethod(HSQOBJECT instance, const char *method_name, bool *res, int suspend)
{
HSQOBJECT ret;
if (!this->CallMethod(instance, method_name, &ret, suspend)) return false;
if (ret._type != OT_BOOL) return false;
*res = ObjectToBool(&ret);
return true;
}
/* static */ bool Squirrel::CreateClassInstanceVM(HSQUIRRELVM vm, const char *class_name, void *real_instance, HSQOBJECT *instance, SQRELEASEHOOK release_hook)
{
int oldtop = sq_gettop(vm);

View File

@ -116,6 +116,7 @@ public:
bool CallMethod(HSQOBJECT instance, const char *method_name, int suspend = -1) { return this->CallMethod(instance, method_name, NULL, suspend); }
bool CallStringMethodStrdup(HSQOBJECT instance, const char *method_name, const char **res, int suspend = -1);
bool CallIntegerMethod(HSQOBJECT instance, const char *method_name, int *res, int suspend = -1);
bool CallBoolMethod(HSQOBJECT instance, const char *method_name, bool *res, int suspend = -1);
/**
* Check if a method exists in an instance.
@ -161,6 +162,11 @@ public:
*/
static int ObjectToInteger(HSQOBJECT *ptr) { return sq_objtointeger(ptr); }
/**
* Convert a Squirrel-object to a bool.
*/
static bool ObjectToBool(HSQOBJECT *ptr) { return sq_objtobool(ptr) == 1; }
/**
* Sets a pointer in the VM that is reachable from where ever you are in SQ.
* Useful to keep track of the main instance.