(svn r19708) -Add: NewGRF "debugging" window and data tables; should be a useful tool for NewGRF developers to get some insights into the value of some variables

replace/41b28d7194a279bdc17475d4fbe2ea6ec885a466
rubidium 14 years ago
parent 1da0086a45
commit 21883a84d7

@ -1183,6 +1183,10 @@
RelativePath=".\..\src\newgrf_config.h"
>
</File>
<File
RelativePath=".\..\src\newgrf_debug.h"
>
</File>
<File
RelativePath=".\..\src\newgrf_engine.h"
>
@ -1851,6 +1855,10 @@
RelativePath=".\..\src\network\network_gui.cpp"
>
</File>
<File
RelativePath=".\..\src\newgrf_debug_gui.cpp"
>
</File>
<File
RelativePath=".\..\src\newgrf_gui.cpp"
>

@ -1180,6 +1180,10 @@
RelativePath=".\..\src\newgrf_config.h"
>
</File>
<File
RelativePath=".\..\src\newgrf_debug.h"
>
</File>
<File
RelativePath=".\..\src\newgrf_engine.h"
>
@ -1848,6 +1852,10 @@
RelativePath=".\..\src\network\network_gui.cpp"
>
</File>
<File
RelativePath=".\..\src\newgrf_debug_gui.cpp"
>
</File>
<File
RelativePath=".\..\src\newgrf_gui.cpp"
>

@ -205,6 +205,7 @@ newgrf_canal.h
newgrf_cargo.h
newgrf_commons.h
newgrf_config.h
newgrf_debug.h
newgrf_engine.h
newgrf_generic.h
newgrf_house.h
@ -389,6 +390,7 @@ music_gui.cpp
network/network_chat_gui.cpp
network/network_content_gui.cpp
network/network_gui.cpp
newgrf_debug_gui.cpp
newgrf_gui.cpp
news_gui.cpp
order_gui.cpp

@ -2365,6 +2365,16 @@ STR_NEWGRF_ADD_FILE_TOOLTIP :{BLACK}Add the
STR_NEWGRF_ADD_RESCAN_FILES :{BLACK}Rescan files
STR_NEWGRF_ADD_RESCAN_FILES_TOOLTIP :{BLACK}Update the list of available NewGRF files
# NewGRF inspect window
STR_NEWGRF_INSPECT_CAPTION :{WHITE}Inspect - {STRING5}
STR_NEWGRF_INSPECT_PARENT_BUTTON :{BLACK}Parent
STR_NEWGRF_INSPECT_PARENT_TOOLTIP :{BLACK}Inspect the object of the parent scope
STR_NEWGRF_INSPECT_CAPTION_OBJECT_AT :{STRING1} at {HEX}
STR_NEWGRF_INSPECT_CAPTION_OBJECT_AT_RAIL_TYPE :Rail type
STR_NEWGRF_INSPECT_QUERY_CAPTION :{WHITE}NewGRF variable 60+x parameter (hexadecimal)
# NewGRF (self) generated warnings/errors
STR_NEWGRF_ERROR_MSG_INFO :{SILVER}{RAW_STRING}
STR_NEWGRF_ERROR_MSG_WARNING :{RED}Warning: {SILVER}{RAW_STRING}

@ -60,6 +60,11 @@ enum GrfSpecFeature {
GSF_RAILTYPES,
GSF_AIRPORTTILES,
GSF_END,
GSF_FAKE_TOWNS = GSF_END, ///< Fake town GrfSpecFeature for NewGRF debugging (parent scope)
GSF_FAKE_END, ///< End of the fake features
GSF_INVALID = 0xFF ///< An invalid spec feature
};
static const uint32 INVALID_GRFID = 0xFFFFFFFF;

@ -0,0 +1,60 @@
/* $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 newgrf_debug.h Functions/types related to NewGRF debugging. */
#ifndef NEWGRF_DEBUG_H
#define NEWGRF_DEBUG_H
#include "newgrf.h"
/**
* Can we inspect the data given a certain feature and index.
* The index is normally an in-game location/identifier, such
* as a TileIndex or an IndustryID depending on the feature
* we want to inspect.
* @param feature The feature we want to inspect.
* @param index The index/identifier of the feature to inspect.
* @return true if there is something to show.
*/
bool IsNewGRFInspectable(GrfSpecFeature feature, uint index);
/**
* Show the inspect window for a given feature and index.
* The index is normally an in-game location/identifier, such
* as a TileIndex or an IndustryID depending on the feature
* we want to inspect.
* @param feature The feature we want to inspect.
* @param index The index/identifier of the feature to inspect.
*/
void ShowNewGRFInspectWindow(GrfSpecFeature feature, uint index);
/**
* Delete inspect window for a given feature and index.
* The index is normally an in-game location/identifier, such
* as a TileIndex or an IndustryID depending on the feature
* we want to inspect.
* @param feature The feature we want to delete the window for.
* @param index The index/identifier of the feature to delete.
*/
void DeleteNewGRFInspectWindow(GrfSpecFeature feature, uint index);
/**
* Get the GrfSpecFeature associated with the tile.
* @return the GrfSpecFeature.
*/
GrfSpecFeature GetGrfSpecFeature(TileIndex tile);
/**
* Get the GrfSpecFeature associated with the vehicle.
* @return the GrfSpecFeature.
*/
GrfSpecFeature GetGrfSpecFeature(VehicleType type);
#endif /* NEWGRF_DEBUG_H */

@ -0,0 +1,530 @@
/* $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 newgrf_debug_gui.cpp GUIs for debugging NewGRFs. */
#include "stdafx.h"
#include <stdarg.h>
#include "window_gui.h"
#include "window_func.h"
#include "gfx_func.h"
#include "string_func.h"
#include "strings_func.h"
#include "textbuf_gui.h"
#include "tunnelbridge_map.h"
#include "engine_base.h"
#include "house.h"
#include "industry.h"
#include "rail.h"
#include "station_base.h"
#include "town.h"
#include "vehicle_base.h"
#include "newgrf_airporttiles.h"
#include "newgrf_debug.h"
#include "newgrf_spritegroup.h"
#include "newgrf_station.h"
#include "newgrf_town.h"
#include "table/strings.h"
/**
* Get the feature index related to the window number.
* @param window_number The window to get the feature index from.
* @return the feature index
*/
static inline uint GetFeatureIndex(uint window_number)
{
return GB(window_number, 0, 24);
}
/**
* Get the window number for the inspect window given a
* feature and index.
* @param feature The feature we want to inspect.
* @param index The index/identifier of the feature to inspect.
* @return the InspectWindow (Window)Number
*/
static inline uint GetInspectWindowNumber(GrfSpecFeature feature, uint index)
{
assert((index >> 24) == 0);
return (feature << 24) | index;
}
/**
* The type of a property to show. This is used to
* provide an appropriate represenation in the GUI.
*/
enum NIType {
NIT_INT, ///< The property is a simple integer
NIT_CARGO, ///< The property is a cargo
};
/** Representation of the data from a NewGRF property. */
struct NIProperty {
const char *name; ///< A (human readable) name for the property
ptrdiff_t offset; ///< Offset of the variable in the class
byte read_size; ///< Number of bytes (i.e. byte, word, dword etc)
byte prop; ///< The number of the property
byte type;
};
/**
* Representation of the available callbacks with
* information on when they actually apply.
*/
struct NICallback {
const char *name; ///< The human readable name of the callback
ptrdiff_t offset; ///< Offset of the variable in the class
byte read_size; ///< The number of bytes (i.e. byte, word, dword etc) to read
byte cb_bit; ///< The bit that needs to be set for this callback to be enabled
uint16 cb_id; ///< The number of the callback
};
/** Mask to show no bit needs to be enabled for the callback. */
static const int CBM_NO_BIT = UINT8_MAX;
/** Representation on the NewGRF variables. */
struct NIVariable {
const char *name;
byte var;
};
/** Helper class to wrap some functionality/queries in. */
class NIHelper {
public:
/**
* Is the item with the given index inspectable?
* @param index the index to check.
* @return true iff the index is inspectable.
*/
virtual bool IsInspectable(uint index) const = 0;
/**
* Get the parent "window_number" of a given instance.
* @param index the instance to get the parent for.
* @return the parent's window_number or UINT32_MAX if there is none.
*/
virtual uint GetParent(uint index) const = 0;
/**
* Get the instance given an index.
* @param index the index to get the instance for.
* @return the instance.
*/
virtual const void *GetInstance(uint index) const = 0;
/**
* Get (NewGRF) specs given an index.
* @param index the index to get the specs for for.
* @return the specs.
*/
virtual const void *GetSpec(uint index) const = 0;
/**
* Set the string parameters to write the right data for a STRINGn.
* @param index the index to get the string parameters for.
*/
virtual void SetStringParameters(uint index) const = 0;
/**
* Resolve (action2) variable for a given index.
* @param index The (instance) index to resolve the variable for.
* @param var The variable to actually resolve.
* @param param The varaction2 0x60+x parameter to pass.
* @param avail Return whether the variable is available.
* @return The resolved variable's value.
*/
virtual uint Resolve(uint index, uint var, uint param, bool *avail) const
{
ResolverObject ro;
memset(&ro, 0, sizeof(ro));
this->Resolve(&ro, index);
return ro.GetVariable(&ro, var, param, avail);
}
protected:
/**
* Actually execute the real resolving for a given (instance) index.
* @param ro The resolver object to fill with everything
* needed to be able to resolve a variable.
* @param index The (instance) index of the to-be-resolved variable.
*/
virtual void Resolve(ResolverObject *ro, uint index) const {}
/**
* Helper to make setting the strings easier.
* @param string the string to actually draw.
* @param index the (instance) index for the string.
*/
void SetSimpleStringParameters(StringID string, uint32 index) const
{
SetDParam(0, string);
SetDParam(1, index);
}
/**
* Helper to make setting the strings easier for objects at a specific tile.
* @param string the string to draw the object's name
* @param index the (instance) index for the string.
* @param tile the tile the object is at
*/
void SetObjectAtStringParameters(StringID string, uint32 index, TileIndex tile) const
{
SetDParam(0, STR_NEWGRF_INSPECT_CAPTION_OBJECT_AT);
SetDParam(1, string);
SetDParam(2, index);
SetDParam(3, tile);
}
};
/** Container for all information for a given feature. */
struct NIFeature {
const NIProperty *properties; ///< The properties associated with this feature.
const NICallback *callbacks; ///< The callbacks associated with this feature.
const NIVariable *variables; ///< The variables associated with this feature.
const NIHelper *helper; ///< The class container all helper functions.
uint psa_size; ///< The size of the persistent storage in indices.
size_t psa_offset; ///< Offset to the array in the PSA.
};
/* Load all the NewGRF debug data; externalised as it is just a huge bunch of tables. */
#include "table/newgrf_debug_data.h"
/**
* Get the NIFeature related to the window number.
* @param window_number The window to get the NIFeature for.
* @return the NIFeature, or NULL is there isn't one.
*/
static inline const NIFeature *GetFeature(uint window_number)
{
byte idx = GB(window_number, 24, 8);
return idx < GSF_FAKE_END ? _nifeatures[idx] : NULL;
}
/**
* Get the NIHelper related to the window number.
* @param window_number The window to get the NIHelper for.
* @pre GetFeature(window_number) != NULL
* @return the NIHelper
*/
static inline const NIHelper *GetFeatureHelper(uint window_number)
{
return GetFeature(window_number)->helper;
}
/** Widget numbers of settings window */
enum NewGRFInspectWidgets {
NIW_CAPTION, ///< The caption bar ofcourse
NIW_PARENT, ///< Inspect the parent
NIW_MAINPANEL, ///< Panel widget containing the actual data
NIW_SCROLLBAR, ///< Scrollbar
};
/** Window used for inspecting NewGRFs. */
struct NewGRFInspectWindow : Window {
static const int LEFT_OFFSET = 5; ///< Position of left edge
static const int RIGHT_OFFSET = 5; ///< Position of right edge
static const int TOP_OFFSET = 5; ///< Position of top edge
static const int BOTTOM_OFFSET = 5; ///< Position of bottom edge
/** The value for the variable 60 parameters. */
static byte var60params[0x20];
/** The currently editted parameter, to update the right one. */
byte current_edit_param;
/**
* Check whether the given variable has a parameter.
* @param variable the variable to check.
* @return true iff the variable has a parameter.
*/
static bool HasVariableParameter(uint variable)
{
return IsInsideBS(variable, 0x60, 0x20);
}
NewGRFInspectWindow(const WindowDesc *desc, WindowNumber wno) : Window()
{
this->InitNested(desc, wno);
this->vscroll.SetCount(0);
this->SetWidgetDisabledState(NIW_PARENT, GetFeatureHelper(this->window_number)->GetParent(GetFeatureIndex(this->window_number)) == UINT32_MAX);
}
virtual void SetStringParameters(int widget) const
{
if (widget != NIW_CAPTION) return;
GetFeatureHelper(this->window_number)->SetStringParameters(GetFeatureIndex(this->window_number));
}
virtual void UpdateWidgetSize(int widget, Dimension *size, const Dimension &padding, Dimension *fill, Dimension *resize)
{
if (widget != NIW_MAINPANEL) return;
resize->height = max(11, FONT_HEIGHT_NORMAL + 1);
resize->width = 1;
size->height = 5 * resize->height + TOP_OFFSET + BOTTOM_OFFSET;
}
/**
* Helper function to draw a string (line) in the window.
* @param r The (screen) rectangle we must draw within
* @param offset The offset (in lines) we want to draw for
* @param format The format string
*/
void WARN_FORMAT(4, 5) DrawString(const Rect &r, int offset, const char *format, ...) const
{
char buf[1024];
va_list va;
va_start(va, format);
vsnprintf(buf, lengthof(buf), format, va);
va_end(va);
offset -= this->vscroll.GetPosition();
if (offset < 0 || offset >= this->vscroll.GetCapacity()) return;
::DrawString(r.left + LEFT_OFFSET, r.right + RIGHT_OFFSET, r.top + TOP_OFFSET + (offset * this->resize.step_height), buf, TC_BLACK);
}
virtual void DrawWidget(const Rect &r, int widget) const
{
if (widget != NIW_MAINPANEL) return;
uint index = GetFeatureIndex(this->window_number);
const NIFeature *nif = GetFeature(this->window_number);
const NIHelper *nih = nif->helper;
const void *base = nih->GetInstance(index);
const void *base_spec = nih->GetSpec(index);
uint i = 0;
if (nif->variables != NULL) {
this->DrawString(r, i++, "Variables:");
for (const NIVariable *niv = nif->variables; niv->name != NULL; niv++) {
bool avail = true;
uint param = HasVariableParameter(niv->var) ? NewGRFInspectWindow::var60params[niv->var - 0x60] : 0;
uint value = nih->Resolve(index, niv->var, param, &avail);
if (!avail) continue;
if (HasVariableParameter(niv->var)) {
this->DrawString(r, i++, " %02x[%02x]: %08x (%s)", niv->var, param, value, niv->name);
} else {
this->DrawString(r, i++, " %02x: %08x (%s)", niv->var, value, niv->name);
}
}
}
if (nif->psa_size != 0) {
this->DrawString(r, i++, "Persistent storage:");
assert(nif->psa_size % 4 == 0);
int32 *psa = (int32*)((byte*)base + nif->psa_offset);
for (uint j = 0; j < nif->psa_size; j += 4, psa += 4) {
this->DrawString(r, i++, " %i: %i %i %i %i", j, psa[0], psa[1], psa[2], psa[3]);
}
}
if (nif->properties != NULL) {
this->DrawString(r, i++, "Properties:");
for (const NIProperty *nip = nif->properties; nip->name != NULL; nip++) {
void *ptr = (byte*)base + nip->offset;
uint value;
switch (nip->read_size) {
case 1: value = *(uint8 *)ptr; break;
case 2: value = *(uint16 *)ptr; break;
case 4: value = *(uint32 *)ptr; break;
default: NOT_REACHED();
}
StringID string;
SetDParam(0, value);
switch (nip->type) {
case NIT_INT:
string = STR_JUST_INT;
break;
case NIT_CARGO:
string = value != INVALID_CARGO ? CargoSpec::Get(value)->name : STR_QUANTITY_N_A;
break;
default:
NOT_REACHED();
}
char buffer[64];
GetString(buffer, string, lastof(buffer));
this->DrawString(r, i++, " %02x: %s (%s)", nip->prop, buffer, nip->name);
}
}
if (nif->callbacks != NULL) {
this->DrawString(r, i++, "Callbacks:");
for (const NICallback *nic = nif->callbacks; nic->name != NULL; nic++) {
if (nic->cb_bit != CBM_NO_BIT) {
void *ptr = (byte*)base_spec + nic->offset;
uint value;
switch (nic->read_size) {
case 1: value = *(uint8 *)ptr; break;
case 2: value = *(uint16 *)ptr; break;
case 4: value = *(uint32 *)ptr; break;
default: NOT_REACHED();
}
if (!HasBit(value, nic->cb_bit)) continue;
this->DrawString(r, i++, " %03x: %s", nic->cb_id, nic->name);
} else {
this->DrawString(r, i++, " %03x: %s (unmasked)", nic->cb_id, nic->name);
}
}
}
/* Not nice and certainly a hack, but it beats duplicating
* this whole function just to count the actual number of
* elements. Especially because they need to be redrawn. */
const_cast<NewGRFInspectWindow*>(this)->vscroll.SetCount(i);
}
virtual void OnPaint()
{
this->DrawWidgets();
}
virtual void OnClick(Point pt, int widget, int click_count)
{
switch (widget) {
case NIW_PARENT: {
uint index = GetFeatureHelper(this->window_number)->GetParent(GetFeatureIndex(this->window_number));
::ShowNewGRFInspectWindow((GrfSpecFeature)GB(index, 24, 8), GetFeatureIndex(index));
} break;
case NIW_MAINPANEL: {
/* Does this feature have variables? */
const NIFeature *nif = GetFeature(this->window_number);
if (nif->variables == NULL) return;
/* Get the line, make sure it's within the boundaries. */
int line = (pt.y - this->GetWidget<NWidgetBase>(NIW_MAINPANEL)->pos_y - TOP_OFFSET) / this->resize.step_height;
if (line >= this->vscroll.GetCapacity()) return;
line += this->vscroll.GetPosition();
/* Find the variable related to the line */
for (const NIVariable *niv = nif->variables; niv->name != NULL; niv++, line--) {
if (line != 1) continue; // 1 because of the "Variables:" line
if (!HasVariableParameter(niv->var)) break;
this->current_edit_param = niv->var;
ShowQueryString(STR_EMPTY, STR_NEWGRF_INSPECT_QUERY_CAPTION, 3, 100, this, CS_HEXADECIMAL, QSF_NONE);
}
}
}
}
virtual void OnQueryTextFinished(char *str)
{
if (StrEmpty(str)) return;
NewGRFInspectWindow::var60params[this->current_edit_param - 0x60] = strtol(str, NULL, 16);
this->SetDirty();
}
virtual void OnResize()
{
this->vscroll.SetCapacityFromWidget(this, NIW_MAINPANEL, TOP_OFFSET + BOTTOM_OFFSET);
}
};
/* static */ byte NewGRFInspectWindow::var60params[0x20] = { 0 }; // Use spec to have 0s in whole array
static const NWidgetPart _nested_newgrf_inspect_widgets[] = {
NWidget(NWID_HORIZONTAL),
NWidget(WWT_CLOSEBOX, COLOUR_GREY),
NWidget(WWT_CAPTION, COLOUR_GREY, NIW_CAPTION), SetDataTip(STR_NEWGRF_INSPECT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS),
NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, NIW_PARENT), SetDataTip(STR_NEWGRF_INSPECT_PARENT_BUTTON, STR_NEWGRF_INSPECT_PARENT_TOOLTIP),
NWidget(WWT_SHADEBOX, COLOUR_GREY),
NWidget(WWT_STICKYBOX, COLOUR_GREY),
EndContainer(),
NWidget(NWID_HORIZONTAL),
NWidget(WWT_PANEL, COLOUR_GREY, NIW_MAINPANEL), SetMinimalSize(300, 0), EndContainer(),
NWidget(NWID_VERTICAL),
NWidget(WWT_SCROLLBAR, COLOUR_GREY, NIW_SCROLLBAR),
NWidget(WWT_RESIZEBOX, COLOUR_GREY),
EndContainer(),
EndContainer(),
};
static const WindowDesc _newgrf_inspect_desc(
WDP_AUTO, 400, 300,
WC_NEWGRF_INSPECT, WC_NONE,
WDF_UNCLICK_BUTTONS,
_nested_newgrf_inspect_widgets, lengthof(_nested_newgrf_inspect_widgets)
);
void ShowNewGRFInspectWindow(GrfSpecFeature feature, uint index)
{
if (!IsNewGRFInspectable(feature, index)) return;
WindowNumber wno = GetInspectWindowNumber(feature, index);
AllocateWindowDescFront<NewGRFInspectWindow>(&_newgrf_inspect_desc, wno);
}
void DeleteNewGRFInspectWindow(GrfSpecFeature feature, uint index)
{
if (feature == GSF_INVALID) return;
WindowNumber wno = GetInspectWindowNumber(feature, index);
DeleteWindowById(WC_NEWGRF_INSPECT, wno);
/* Reinitialise the land information window to remove the "debug" sprite if needed. */
Window *w = FindWindowById(WC_LAND_INFO, 0);
if (w != NULL) w->ReInit();
}
bool IsNewGRFInspectable(GrfSpecFeature feature, uint index)
{
const NIFeature *nif = GetFeature(GetInspectWindowNumber(feature, index));
if (nif == NULL) return false;
return nif->helper->IsInspectable(index);
}
GrfSpecFeature GetGrfSpecFeature(TileIndex tile)
{
switch (GetTileType(tile)) {
default: return GSF_INVALID;
case MP_RAILWAY: return GSF_RAILTYPES;
case MP_ROAD: return IsLevelCrossing(tile) ? GSF_RAILTYPES : GSF_INVALID;
case MP_HOUSE: return GSF_HOUSES;
case MP_INDUSTRY: return GSF_INDUSTRYTILES;
case MP_STATION:
switch (GetStationType(tile)) {
case STATION_RAIL: return GSF_STATIONS;
case STATION_AIRPORT: return GSF_AIRPORTTILES;
default: return GSF_INVALID;
}
}
}
GrfSpecFeature GetGrfSpecFeature(VehicleType type)
{
switch (type) {
case VEH_TRAIN: return GSF_TRAINS;
case VEH_ROAD: return GSF_ROADVEHICLES;
case VEH_SHIP: return GSF_SHIPS;
case VEH_AIRCRAFT: return GSF_AIRCRAFT;
default: return GSF_INVALID;
}
}

@ -0,0 +1,426 @@
/* $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 newgrf_debug_data.cpp Data 'tables' for NewGRF debugging. */
/* Helper for filling property tables */
#define NIP(prop, base, variable, type, name) { name, cpp_offsetof(base, variable), cpp_sizeof(base, variable), prop, type }
#define NIP_END() { NULL, 0, 0, 0, 0 }
/* Helper for filling callback tables */
#define NIC(cb_id, base, variable, bit) { #cb_id, cpp_offsetof(base, variable), cpp_sizeof(base, variable), bit, cb_id }
#define NIC_END() { NULL, 0, 0, 0, 0 }
/* Helper for filling variable tables */
#define NIV(var, name) { name, var }
#define NIV_END() { NULL, 0 }
/*** NewGRF Vehicles ***/
#define NICV(cb_id, bit) NIC(cb_id, Engine, info.callback_mask, bit)
static const NICallback _nic_vehicles[] = {
NICV(CBID_TRAIN_WAGON_POWER, CBM_TRAIN_WAGON_POWER),
NICV(CBID_VEHICLE_LENGTH, CBM_VEHICLE_LENGTH),
NICV(CBID_VEHICLE_LOAD_AMOUNT, CBM_VEHICLE_LOAD_AMOUNT),
NICV(CBID_VEHICLE_REFIT_CAPACITY, CBM_VEHICLE_REFIT_CAPACITY),
NICV(CBID_VEHICLE_ARTIC_ENGINE, CBM_VEHICLE_ARTIC_ENGINE),
NICV(CBID_VEHICLE_CARGO_SUFFIX, CBM_VEHICLE_CARGO_SUFFIX),
NICV(CBID_TRAIN_ALLOW_WAGON_ATTACH, CBM_NO_BIT),
NICV(CBID_VEHICLE_ADDITIONAL_TEXT, CBM_NO_BIT),
NICV(CBID_VEHICLE_COLOUR_MAPPING, CBM_VEHICLE_COLOUR_REMAP),
NICV(CBID_VEHICLE_START_STOP_CHECK, CBM_NO_BIT),
NICV(CBID_VEHICLE_32DAY_CALLBACK, CBM_NO_BIT),
NICV(CBID_VEHICLE_SOUND_EFFECT, CBM_VEHICLE_SOUND_EFFECT),
NICV(CBID_VEHICLE_AUTOREPLACE_SELECTION, CBM_NO_BIT),
NICV(CBID_VEHICLE_MODIFY_PROPERTY, CBM_NO_BIT),
NIC_END()
};
static const NIVariable _niv_vehicles[] = {
NIV(0x40, "position in consist and length"),
NIV(0x41, "position and length of chain of same vehicles"),
NIV(0x42, "transported cargo types"),
NIV(0x43, "player info"),
NIV(0x44, "aircraft info"),
NIV(0x45, "curvature info"),
NIV(0x46, "motion counter"),
NIV(0x47, "vehicle cargo info"),
NIV(0x48, "vehicle type info"),
NIV(0x49, "year of construction"),
NIV(0x60, "count vehicle id occurrences"),
NIV_END()
};
class NIHVehicle : public NIHelper {
bool IsInspectable(uint index) const { return Engine::Get(Vehicle::Get(index)->engine_type)->grffile != NULL; }
uint GetParent(uint index) const { const Vehicle *first = Vehicle::Get(index)->First(); return GetInspectWindowNumber(GetGrfSpecFeature(first->type), first->index); }
const void *GetInstance(uint index)const { return Vehicle::Get(index); }
const void *GetSpec(uint index) const { return Engine::Get(Vehicle::Get(index)->engine_type); }
void SetStringParameters(uint index) const { this->SetSimpleStringParameters(STR_VEHICLE_NAME, index); }
void Resolve(ResolverObject *ro, uint32 index) const { extern void GetVehicleResolver(ResolverObject *ro, uint index); GetVehicleResolver(ro, index); }
};
static const NIFeature _nif_vehicle = {
NULL,
_nic_vehicles,
_niv_vehicles,
new NIHVehicle(),
0,
0
};
/*** NewGRF station (tiles) ***/
#define NICS(cb_id, bit) NIC(cb_id, StationSpec, callback_mask, bit)
static const NICallback _nic_stations[] = {
NICS(CBID_STATION_AVAILABILITY, CBM_STATION_AVAIL),
NICS(CBID_STATION_SPRITE_LAYOUT, CBM_NO_BIT),
NICS(CBID_STATION_TILE_LAYOUT, CBM_STATION_SPRITE_LAYOUT),
NICS(CBID_STATION_ANIM_START_STOP, CBM_NO_BIT),
NICS(CBID_STATION_ANIM_NEXT_FRAME, CBM_STATION_ANIMATION_NEXT_FRAME),
NICS(CBID_STATION_ANIMATION_SPEED, CBM_STATION_ANIMATION_SPEED),
NICS(CBID_STATION_LAND_SLOPE_CHECK, CBM_STATION_SLOPE_CHECK),
NIC_END()
};
static const NIVariable _niv_stations[] = {
NIV(0x40, "platform info and relative position"),
NIV(0x41, "platform info and relative position for individually built sections"),
NIV(0x42, "terrain and track type"),
NIV(0x43, "player info"),
NIV(0x44, "path signalling info"),
NIV(0x45, "rail continuation info"),
NIV(0x46, "platform info and relative position from middle"),
NIV(0x47, "platform info and relative position from middle for individually built sections"),
NIV(0x48, "bitmask of accepted cargoes"),
NIV(0x49, "platform info and relative position of same-direction section"),
NIV(0x4A, "current animation frame"),
NIV(0x60, "amount of cargo waiting"),
NIV(0x61, "time since last cargo pickup"),
NIV(0x62, "rating of cargo"),
NIV(0x63, "time spent on route"),
NIV(0x64, "information about last vehicle picking cargo up"),
NIV(0x65, "amount of cargo acceptance"),
NIV(0x66, "animation frame of nearby tile"),
NIV(0x67, "land info of nearby tiles"),
NIV(0x68, "station info of nearby tiles"),
NIV(0x69, "information about cargo accepted in the past"),
NIV_END()
};
class NIHStation : public NIHelper {
bool IsInspectable(uint index) const { return GetStationSpec(index) != NULL; }
uint GetParent(uint index) const { return GetInspectWindowNumber(GSF_FAKE_TOWNS, Station::GetByTile(index)->town->index); }
const void *GetInstance(uint index)const { return NULL; }
const void *GetSpec(uint index) const { return GetStationSpec(index); }
void SetStringParameters(uint index) const { this->SetObjectAtStringParameters(STR_STATION_NAME, GetStationIndex(index), index); }
void Resolve(ResolverObject *ro, uint32 index) const { extern void GetStationResolver(ResolverObject *ro, uint index); GetStationResolver(ro, index); }
};
static const NIFeature _nif_station = {
NULL,
_nic_stations,
_niv_stations,
new NIHStation(),
0,
0
};
/*** NewGRF house tiles ***/
#define NICH(cb_id, bit) NIC(cb_id, HouseSpec, callback_mask, bit)
static const NICallback _nic_house[] = {
NICH(CBID_HOUSE_ALLOW_CONSTRUCTION, CBM_HOUSE_ALLOW_CONSTRUCTION),
NICH(CBID_HOUSE_ANIMATION_NEXT_FRAME, CBM_HOUSE_ANIMATION_NEXT_FRAME),
NICH(CBID_HOUSE_ANIMATION_START_STOP, CBM_HOUSE_ANIMATION_START_STOP),
NICH(CBID_HOUSE_CONSTRUCTION_STATE_CHANGE, CBM_HOUSE_CONSTRUCTION_STATE_CHANGE),
NICH(CBID_HOUSE_COLOUR, CBM_HOUSE_COLOUR),
NICH(CBID_HOUSE_CARGO_ACCEPTANCE, CBM_HOUSE_CARGO_ACCEPTANCE),
NICH(CBID_HOUSE_ANIMATION_SPEED, CBM_HOUSE_ANIMATION_SPEED),
NICH(CBID_HOUSE_DESTRUCTION, CBM_HOUSE_DESTRUCTION),
NICH(CBID_HOUSE_ACCEPT_CARGO, CBM_HOUSE_ACCEPT_CARGO),
NICH(CBID_HOUSE_PRODUCE_CARGO, CBM_HOUSE_PRODUCE_CARGO),
NICH(CBID_HOUSE_DENY_DESTRUCTION, CBM_HOUSE_DENY_DESTRUCTION),
NICH(CBID_HOUSE_WATCHED_CARGO_ACCEPTED, CBM_NO_BIT),
NICH(CBID_HOUSE_CUSTOM_NAME, CBM_NO_BIT),
NICH(CBID_HOUSE_DRAW_FOUNDATIONS, CBM_HOUSE_DRAW_FOUNDATIONS),
NICH(CBID_HOUSE_AUTOSLOPE, CBM_HOUSE_AUTOSLOPE),
NIC_END()
};
static const NIVariable _niv_house[] = {
NIV(0x40, "construction state of tile and pseudo-random value"),
NIV(0x41, "age of building in years"),
NIV(0x42, "town zone"),
NIV(0x43, "terrain type"),
NIV(0x44, "building counts"),
NIV(0x45, "town expansion bits"),
NIV(0x46, "current animation frame"),
NIV(0x47, "xy coordinate of the building"),
NIV(0x60, "other building counts (old house type)"),
NIV(0x61, "other building counts (new house type)"),
NIV(0x62, "land info of nearby tiles"),
NIV(0x63, "current animation frame of nearby house tile"),
NIV(0x64, "cargo acceptance history of nearby stations"),
NIV(0x65, "distance of nearest house matching a given criterion"),
NIV(0x66, "class and ID of nearby house tile"),
NIV(0x67, "GRFID of nearby house tile"),
NIV_END()
};
class NIHHouse : public NIHelper {
bool IsInspectable(uint index) const { return HouseSpec::Get(GetHouseType(index))->grffile != NULL; }
uint GetParent(uint index) const { return GetInspectWindowNumber(GSF_FAKE_TOWNS, GetTownIndex(index)); }
const void *GetInstance(uint index)const { return NULL; }
const void *GetSpec(uint index) const { return HouseSpec::Get(GetHouseType(index)); }
void SetStringParameters(uint index) const { this->SetObjectAtStringParameters(STR_TOWN_NAME, GetTownIndex(index), index); }
void Resolve(ResolverObject *ro, uint32 index) const { extern void GetHouseResolver(ResolverObject *ro, uint index); GetHouseResolver(ro, index); }
};
static const NIFeature _nif_house = {
NULL,
_nic_house,
_niv_house,
new NIHHouse(),
0,
0
};
/*** NewGRF industry tiles ***/
#define NICIT(cb_id, bit) NIC(cb_id, IndustryTileSpec, callback_mask, bit)
static const NICallback _nic_industrytiles[] = {
NICIT(CBID_INDTILE_ANIM_START_STOP, CBM_NO_BIT),
NICIT(CBID_INDTILE_ANIM_NEXT_FRAME, CBM_INDT_ANIM_NEXT_FRAME),
NICIT(CBID_INDTILE_ANIMATION_SPEED, CBM_INDT_ANIM_SPEED),
NICIT(CBID_INDTILE_CARGO_ACCEPTANCE, CBM_INDT_CARGO_ACCEPTANCE),
NICIT(CBID_INDTILE_ACCEPT_CARGO, CBM_INDT_ACCEPT_CARGO),
NICIT(CBID_INDTILE_SHAPE_CHECK, CBM_INDT_SHAPE_CHECK),
NICIT(CBID_INDTILE_DRAW_FOUNDATIONS, CBM_INDT_DRAW_FOUNDATIONS),
NICIT(CBID_INDTILE_AUTOSLOPE, CBM_INDT_AUTOSLOPE),
NIC_END()
};
static const NIVariable _niv_industrytiles[] = {
NIV(0x40, "construction state of tile"),
NIV(0x41, "ground type"),
NIV(0x42, "current town zone in nearest town"),
NIV(0x43, "relative position"),
NIV(0x44, "animation frame"),
NIV(0x60, "land info of nearby tiles"),
NIV(0x61, "animation stage of nearby tiles"),
NIV(0x62, "get industry or airport tile ID at offset"),
NIV_END()
};
class NIHIndustryTile : public NIHelper {
bool IsInspectable(uint index) const { return GetIndustryTileSpec(GetIndustryGfx(index))->grf_prop.grffile != NULL; }
uint GetParent(uint index) const { return GetInspectWindowNumber(GSF_INDUSTRIES, GetIndustryIndex(index)); }
const void *GetInstance(uint index)const { return NULL; }
const void *GetSpec(uint index) const { return GetIndustryTileSpec(GetIndustryGfx(index)); }
void SetStringParameters(uint index) const { this->SetObjectAtStringParameters(STR_INDUSTRY_NAME, GetIndustryIndex(index), index); }
void Resolve(ResolverObject *ro, uint32 index) const { extern void GetIndustryTileResolver(ResolverObject *ro, uint index); GetIndustryTileResolver(ro, index); }
};
static const NIFeature _nif_industrytile = {
NULL,
_nic_industrytiles,
_niv_industrytiles,
new NIHIndustryTile(),
0,
0
};
/*** NewGRF industries ***/
static const NIProperty _nip_industries[] = {
NIP(0x10, Industry, produced_cargo[0], NIT_CARGO, "produced cargo 0"),
NIP(0x10, Industry, produced_cargo[1], NIT_CARGO, "produced cargo 1"),
NIP(0x11, Industry, accepts_cargo[0], NIT_CARGO, "accepted cargo 0"),
NIP(0x11, Industry, accepts_cargo[1], NIT_CARGO, "accepted cargo 1"),
NIP(0x11, Industry, accepts_cargo[2], NIT_CARGO, "accepted cargo 2"),
NIP_END()
};
#define NICI(cb_id, bit) NIC(cb_id, IndustrySpec, callback_mask, bit)
static const NICallback _nic_industries[] = {
NICI(CBID_INDUSTRY_AVAILABLE, CBM_IND_AVAILABLE),
NICI(CBID_INDUSTRY_LOCATION, CBM_IND_LOCATION),
NICI(CBID_INDUSTRY_PRODUCTION_CHANGE, CBM_IND_PRODUCTION_CHANGE),
NICI(CBID_INDUSTRY_MONTHLYPROD_CHANGE, CBM_IND_MONTHLYPROD_CHANGE),
NICI(CBID_INDUSTRY_CARGO_SUFFIX, CBM_IND_CARGO_SUFFIX),
NICI(CBID_INDUSTRY_FUND_MORE_TEXT, CBM_IND_FUND_MORE_TEXT),
NICI(CBID_INDUSTRY_WINDOW_MORE_TEXT, CBM_IND_WINDOW_MORE_TEXT),
NICI(CBID_INDUSTRY_SPECIAL_EFFECT, CBM_IND_SPECIAL_EFFECT),
NICI(CBID_INDUSTRY_REFUSE_CARGO, CBM_IND_REFUSE_CARGO),
NICI(CBID_INDUSTRY_DECIDE_COLOUR, CBM_IND_DECIDE_COLOUR),
NICI(CBID_INDUSTRY_INPUT_CARGO_TYPES, CBM_IND_INPUT_CARGO_TYPES),
NICI(CBID_INDUSTRY_OUTPUT_CARGO_TYPES, CBM_IND_OUTPUT_CARGO_TYPES),
NIC_END()
};
static const NIVariable _niv_industries[] = {
NIV(0x40, "waiting cargo 0"),
NIV(0x41, "waiting cargo 1"),
NIV(0x42, "waiting cargo 2"),
NIV(0x43, "distance to closest dry/land tile"),
NIV(0x44, "layout number"),
NIV(0x45, "player info"),
NIV(0x46, "industry construction date"),
NIV(0x60, "get industry tile ID at offset"),
NIV(0x61, "get random tile bits at offset"),
NIV(0x62, "land info of nearby tiles"),
NIV(0x63, "animation stage of nearby tiles"),
NIV(0x64, "distance on nearest industry with given type"),
NIV(0x65, "get town zone and Manhattan distance of closest town"),
NIV(0x66, "get square of Euclidean distance of closes town"),
NIV(0x67, "count of industry and distance of closest instance"),
NIV(0x68, "count of industry and distance of closest instance with layout filter"),
NIV_END()
};
class NIHIndustry : public NIHelper {
bool IsInspectable(uint index) const { return GetIndustrySpec(Industry::Get(index)->type)->grf_prop.grffile != NULL; }
uint GetParent(uint index) const { return GetInspectWindowNumber(GSF_FAKE_TOWNS, Industry::Get(index)->town->index); }
const void *GetInstance(uint index)const { return Industry::Get(index); }
const void *GetSpec(uint index) const { return GetIndustrySpec(Industry::Get(index)->type); }
void SetStringParameters(uint index) const { this->SetSimpleStringParameters(STR_INDUSTRY_NAME, index); }
void Resolve(ResolverObject *ro, uint32 index) const { extern void GetIndustryResolver(ResolverObject *ro, uint index); GetIndustryResolver(ro, index); }
};
static const NIFeature _nif_industry = {
_nip_industries,
_nic_industries,
_niv_industries,
new NIHIndustry(),
cpp_lengthof(Industry, psa.storage),
cpp_offsetof(Industry, psa.storage)
};
/*** NewGRF rail types ***/
static const NIVariable _niv_railtypes[] = {
NIV(0x40, "terrain type"),
NIV(0x41, "enhanced tunnels"),
NIV(0x42, "level crossing status"),
NIV_END()
};
class NIHRailType : public NIHelper {
bool IsInspectable(uint index) const { return true; }
uint GetParent(uint index) const { return UINT32_MAX; }
const void *GetInstance(uint index)const { return NULL; }
const void *GetSpec(uint index) const { return NULL; }
void SetStringParameters(uint index) const { this->SetObjectAtStringParameters(STR_NEWGRF_INSPECT_CAPTION_OBJECT_AT_RAIL_TYPE, INVALID_STRING_ID, index); }
void Resolve(ResolverObject *ro, uint32 index) const { extern void GetRailTypeResolver(ResolverObject *ro, uint index); GetRailTypeResolver(ro, index); }
};
static const NIFeature _nif_railtype = {
NULL,
NULL,
_niv_railtypes,
new NIHRailType(),
0,
0
};
/*** NewGRF airport tiles ***/
#define NICAT(cb_id, bit) NIC(cb_id, AirportTileSpec, callback_flags, bit)
static const NICallback _nic_airporttiles[] = {
NICAT(CBID_AIRPTILE_DRAW_FOUNDATIONS, CBM_AIRT_DRAW_FOUNDATIONS),
NICAT(CBID_AIRPTILE_ANIM_START_STOP, CBM_NO_BIT),
NICAT(CBID_AIRPTILE_ANIM_NEXT_FRAME, CBM_AIRT_ANIM_NEXT_FRAME),
NICAT(CBID_AIRPTILE_ANIMATION_SPEED, CBM_AIRT_ANIM_SPEED),
NIC_END()
};
class NIHAirportTile : public NIHelper {
bool IsInspectable(uint index) const { return AirportTileSpec::Get(GetAirportGfx(index))->grf_prop.grffile != NULL; }
uint GetParent(uint index) const { return GetInspectWindowNumber(GSF_FAKE_TOWNS, Station::GetByTile(index)->town->index); }
const void *GetInstance(uint index)const { return NULL; }
const void *GetSpec(uint index) const { return AirportTileSpec::Get(GetAirportGfx(index)); }
void SetStringParameters(uint index) const { this->SetObjectAtStringParameters(STR_STATION_NAME, GetStationIndex(index), index); }
void Resolve(ResolverObject *ro, uint32 index) const { extern void GetAirportTileTypeResolver(ResolverObject *ro, uint index); GetAirportTileTypeResolver(ro, index); }
};
static const NIFeature _nif_airporttile = {
NULL,
_nic_airporttiles,
_niv_industrytiles, // Yes, they share this (at least now)
new NIHAirportTile(),
0,
0
};
/*** NewGRF towns ***/
static const NIVariable _niv_towns[] = {
NIV(0x40, "larger town effect on this town"),
NIV(0x41, "town index"),
NIV(0x82, "population"),
NIV(0x94, "zone radius 0"),
NIV(0x96, "zone radius 1"),
NIV(0x98, "zone radius 2"),
NIV(0x9A, "zone radius 3"),
NIV(0x9C, "zone radius 4"),
NIV(0xB6, "number of buildings"),
NIV_END()
};
class NIHTown : public NIHelper {
bool IsInspectable(uint index) const { return false; }
uint GetParent(uint index) const { return UINT32_MAX; }
const void *GetInstance(uint index)const { return Town::Get(index); }
const void *GetSpec(uint index) const { return NULL; }
void SetStringParameters(uint index) const { this->SetSimpleStringParameters(STR_TOWN_NAME, index); }
uint Resolve(uint index, uint var, uint param, bool *avail) const { return TownGetVariable(var, param, avail, Town::Get(index)); }
};
static const NIFeature _nif_town = {
NULL,
NULL,
_niv_towns,
new NIHTown(),
0,
0
};
/** Table with all NIFeatures. */
static const NIFeature * const _nifeatures[] = {
&_nif_vehicle, // GSF_TRAINS
&_nif_vehicle, // GSF_ROADVEHICLES
&_nif_vehicle, // GSF_SHIPS
&_nif_vehicle, // GSF_AIRCRAFT
&_nif_station, // GSF_STATIONS
NULL, // GSF_CANALS (no callbacks/action2 implemented)
NULL, // GSF_BRIDGES (no callbacks/action2)
&_nif_house, // GSF_HOUSES
NULL, // GSF_GLOBALVAR (has no "physical" objects)
&_nif_industrytile, // GSF_INDUSTRYTILES
&_nif_industry, // GSF_INDUSTRIES
NULL, // GSF_CARGOS (has no "physical" objects)
NULL, // GSF_SOUNDFX (has no "physical" objects)
NULL, // GSF_AIRPORTS (feature not implemented)
NULL, // GSF_SIGNALS (feature not implemented)
NULL, // GSF_OBJECTS (feature not implemented)
&_nif_railtype, // GSF_RAILTYPES
&_nif_airporttile, // GSF_AIRPORTTILES
&_nif_town, // GSF_FAKE_TOWNS
};
assert_compile(lengthof(_nifeatures) == GSF_FAKE_END);

@ -106,6 +106,7 @@ enum WindowClass {
WC_AI_DEBUG,
WC_AI_LIST,
WC_AI_SETTINGS,
WC_NEWGRF_INSPECT,
WC_INVALID = 0xFFFF
};

Loading…
Cancel
Save