4 * This file is part of OpenTTD.
5 * 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.
6 * 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.
7 * 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/>.
10 /** @file newgrf_debug_gui.cpp GUIs for debugging NewGRFs. */
14 #include "window_gui.h"
15 #include "window_func.h"
16 #include "fileio_func.h"
17 #include "spritecache.h"
19 #include "strings_func.h"
20 #include "textbuf_gui.h"
21 #include "vehicle_gui.h"
22 #include "zoom_func.h"
24 #include "engine_base.h"
26 #include "object_base.h"
27 #include "station_base.h"
29 #include "vehicle_base.h"
30 #include "map/depot.h"
34 #include "newgrf_airporttiles.h"
35 #include "newgrf_debug.h"
36 #include "newgrf_object.h"
37 #include "newgrf_spritegroup.h"
38 #include "newgrf_station.h"
39 #include "newgrf_town.h"
40 #include "newgrf_railtype.h"
41 #include "newgrf_industries.h"
42 #include "newgrf_industrytiles.h"
44 #include "widgets/newgrf_debug_widget.h"
46 #include "table/strings.h"
48 /** The sprite picker. */
49 NewGrfDebugSpritePicker _newgrf_debug_sprite_picker
= { SPM_NONE
, NULL
, 0, SmallVector
<SpriteID
, 256>() };
52 * Get the feature index related to the window number.
53 * @param window_number The window to get the feature index from.
54 * @return the feature index
56 static inline uint
GetFeatureIndex(uint window_number
)
58 return GB(window_number
, 0, 24);
62 * Get the window number for the inspect window given a
64 * @param feature The feature we want to inspect.
65 * @param index The index/identifier of the feature to inspect.
66 * @return the InspectWindow (Window)Number
68 static inline uint
GetInspectWindowNumber(GrfSpecFeature feature
, uint index
)
70 assert((index
>> 24) == 0);
71 return (feature
<< 24) | index
;
75 * The type of a property to show. This is used to
76 * provide an appropriate representation in the GUI.
79 NIT_INT
, ///< The property is a simple integer
80 NIT_CARGO
, ///< The property is a cargo
83 /** Representation of the data from a NewGRF property. */
85 const char *name
; ///< A (human readable) name for the property
86 ptrdiff_t offset
; ///< Offset of the variable in the class
87 byte read_size
; ///< Number of bytes (i.e. byte, word, dword etc)
88 byte prop
; ///< The number of the property
94 * Representation of the available callbacks with
95 * information on when they actually apply.
98 const char *name
; ///< The human readable name of the callback
99 ptrdiff_t offset
; ///< Offset of the variable in the class
100 byte read_size
; ///< The number of bytes (i.e. byte, word, dword etc) to read
101 byte cb_bit
; ///< The bit that needs to be set for this callback to be enabled
102 uint16 cb_id
; ///< The number of the callback
104 /** Mask to show no bit needs to be enabled for the callback. */
105 static const int CBM_NO_BIT
= UINT8_MAX
;
107 /** Representation on the NewGRF variables. */
113 /** Helper class to wrap some functionality/queries in. */
116 /** Silence a warning. */
117 virtual ~NIHelper() {}
120 * Is the item with the given index inspectable?
121 * @param index the index to check.
122 * @return true iff the index is inspectable.
124 virtual bool IsInspectable(uint index
) const = 0;
127 * Get the parent "window_number" of a given instance.
128 * @param index the instance to get the parent for.
129 * @return the parent's window_number or UINT32_MAX if there is none.
131 virtual uint
GetParent(uint index
) const = 0;
134 * Get the instance given an index.
135 * @param index the index to get the instance for.
136 * @return the instance.
138 virtual const void *GetInstance(uint index
) const = 0;
141 * Get (NewGRF) specs given an index.
142 * @param index the index to get the specs for for.
145 virtual const void *GetSpec(uint index
) const = 0;
148 * Set the string parameters to write the right data for a STRINGn.
149 * @param index the index to get the string parameters for.
151 virtual void SetStringParameters(uint index
) const = 0;
154 * Get the GRFID of the file that includes this item.
155 * @param index index to check.
156 * @return GRFID of the item. 0 means that the item is not inspectable.
158 virtual uint32
GetGRFID(uint index
) const = 0;
161 * Resolve (action2) variable for a given index.
162 * @param index The (instance) index to resolve the variable for.
163 * @param var The variable to actually resolve.
164 * @param param The varaction2 0x60+x parameter to pass.
165 * @param avail Return whether the variable is available.
166 * @return The resolved variable's value.
168 virtual uint
Resolve(uint index
, uint var
, uint param
, bool *avail
) const = 0;
171 * Used to decide if the PSA needs a parameter or not.
172 * @return True iff this item has a PSA that requires a parameter.
174 virtual bool PSAWithParameter() const
180 * Allows to know the size of the persistent storage.
181 * @param index Index of the item.
182 * @param grfid Parameter for the PSA. Only required for items with parameters.
183 * @return Size of the persistent storage in indices.
185 virtual uint
GetPSASize(uint index
, uint32 grfid
) const
191 * Gets the first position of the array containing the persistent storage.
192 * @param index Index of the item.
193 * @param grfid Parameter for the PSA. Only required for items with parameters.
194 * @return Pointer to the first position of the storage array or NULL if not present.
196 virtual const int32
*GetPSAFirstPosition(uint index
, uint32 grfid
) const
203 * Helper to make setting the strings easier.
204 * @param string the string to actually draw.
205 * @param index the (instance) index for the string.
207 void SetSimpleStringParameters(StringID string
, uint32 index
) const
209 SetDParam(0, string
);
215 * Helper to make setting the strings easier for objects at a specific tile.
216 * @param string the string to draw the object's name
217 * @param index the (instance) index for the string.
218 * @param tile the tile the object is at
220 void SetObjectAtStringParameters(StringID string
, uint32 index
, TileIndex tile
) const
222 SetDParam(0, STR_NEWGRF_INSPECT_CAPTION_OBJECT_AT
);
223 SetDParam(1, string
);
230 /** Container for all information for a given feature. */
232 const NIProperty
*properties
; ///< The properties associated with this feature.
233 const NICallback
*callbacks
; ///< The callbacks associated with this feature.
234 const NIVariable
*variables
; ///< The variables associated with this feature.
235 const NIHelper
*helper
; ///< The class container all helper functions.
238 /* Load all the NewGRF debug data; externalised as it is just a huge bunch of tables. */
239 #include "table/newgrf_debug_data.h"
242 * Get the feature number related to the window number.
243 * @param window_number The window to get the feature number for.
244 * @return The feature number.
246 static inline GrfSpecFeature
GetFeatureNum(uint window_number
)
248 return (GrfSpecFeature
)GB(window_number
, 24, 8);
252 * Get the NIFeature related to the window number.
253 * @param window_number The window to get the NIFeature for.
254 * @return the NIFeature, or NULL is there isn't one.
256 static inline const NIFeature
*GetFeature(uint window_number
)
258 GrfSpecFeature idx
= GetFeatureNum(window_number
);
259 return idx
< GSF_FAKE_END
? _nifeatures
[idx
] : NULL
;
263 * Get the NIHelper related to the window number.
264 * @param window_number The window to get the NIHelper for.
265 * @pre GetFeature(window_number) != NULL
266 * @return the NIHelper
268 static inline const NIHelper
*GetFeatureHelper(uint window_number
)
270 return GetFeature(window_number
)->helper
;
273 /** Window used for inspecting NewGRFs. */
274 struct NewGRFInspectWindow
: Window
{
275 static const int LEFT_OFFSET
= 5; ///< Position of left edge
276 static const int RIGHT_OFFSET
= 5; ///< Position of right edge
277 static const int TOP_OFFSET
= 5; ///< Position of top edge
278 static const int BOTTOM_OFFSET
= 5; ///< Position of bottom edge
280 /** The value for the variable 60 parameters. */
281 static uint32 var60params
[GSF_FAKE_END
][0x20];
283 /** GRFID of the caller of this window, 0 if it has no caller. */
286 /** For ground vehicles: Index in vehicle chain. */
289 /** The currently edited parameter, to update the right one. */
290 byte current_edit_param
;
295 * Check whether the given variable has a parameter.
296 * @param variable the variable to check.
297 * @return true iff the variable has a parameter.
299 static bool HasVariableParameter(uint variable
)
301 return IsInsideBS(variable
, 0x60, 0x20);
305 * Set the GRFID of the item opening this window.
306 * @param grfid GRFID of the item opening this window, or 0 if not opened by other window.
308 void SetCallerGRFID(uint32 grfid
)
310 this->caller_grfid
= grfid
;
315 * Check whether this feature has chain index, i.e. refers to ground vehicles.
317 bool HasChainIndex() const
319 GrfSpecFeature f
= GetFeatureNum(this->window_number
);
320 return f
== GSF_TRAINS
|| f
== GSF_ROADVEHICLES
;
324 * Get the feature index.
325 * @return the feature index
327 uint
GetFeatureIndex() const
329 uint index
= ::GetFeatureIndex(this->window_number
);
330 if (this->chain_index
> 0) {
331 assert(this->HasChainIndex());
332 const Vehicle
*v
= Vehicle::Get(index
);
333 v
= v
->Move(this->chain_index
);
334 if (v
!= NULL
) index
= v
->index
;
340 * Ensure that this->chain_index is in range.
342 void ValidateChainIndex()
344 if (this->chain_index
== 0) return;
346 assert(this->HasChainIndex());
348 const Vehicle
*v
= Vehicle::Get(::GetFeatureIndex(this->window_number
));
349 v
= v
->Move(this->chain_index
);
350 if (v
== NULL
) this->chain_index
= 0;
353 NewGRFInspectWindow (const WindowDesc
*desc
, WindowNumber wno
) :
354 Window (desc
), caller_grfid (0), chain_index (0),
355 current_edit_param (0), vscroll (NULL
)
357 this->CreateNestedTree();
358 this->vscroll
= this->GetScrollbar(WID_NGRFI_SCROLLBAR
);
359 this->InitNested(wno
);
361 this->vscroll
->SetCount(0);
362 this->SetWidgetDisabledState(WID_NGRFI_PARENT
, GetFeatureHelper(this->window_number
)->GetParent(this->GetFeatureIndex()) == UINT32_MAX
);
364 this->OnInvalidateData(0, true);
367 virtual void SetStringParameters(int widget
) const
369 if (widget
!= WID_NGRFI_CAPTION
) return;
371 GetFeatureHelper(this->window_number
)->SetStringParameters(this->GetFeatureIndex());
374 virtual void UpdateWidgetSize(int widget
, Dimension
*size
, const Dimension
&padding
, Dimension
*fill
, Dimension
*resize
)
377 case WID_NGRFI_VEH_CHAIN
: {
378 assert(this->HasChainIndex());
379 GrfSpecFeature f
= GetFeatureNum(this->window_number
);
380 size
->height
= max(size
->height
, GetVehicleImageCellSize((VehicleType
)(VEH_TRAIN
+ (f
- GSF_TRAINS
)), EIT_IN_DEPOT
).height
+ 2 + WD_BEVEL_TOP
+ WD_BEVEL_BOTTOM
);
384 case WID_NGRFI_MAINPANEL
:
385 resize
->height
= max(11, FONT_HEIGHT_NORMAL
+ 1);
388 size
->height
= 5 * resize
->height
+ TOP_OFFSET
+ BOTTOM_OFFSET
;
394 * Helper function to draw a string (line) in the window.
395 * @param dpi The area to draw on
396 * @param r The (screen) rectangle we must draw within
397 * @param offset The offset (in lines) we want to draw for
398 * @param format The format string
400 void WARN_FORMAT(5, 6) DrawString (BlitArea
*dpi
, const Rect
&r
,
401 int offset
, const char *format
, ...) const
406 va_start(va
, format
);
407 bstrvfmt (buf
, format
, va
);
410 offset
-= this->vscroll
->GetPosition();
411 if (offset
< 0 || offset
>= this->vscroll
->GetCapacity()) return;
413 ::DrawString (dpi
, r
.left
+ LEFT_OFFSET
, r
.right
- RIGHT_OFFSET
, r
.top
+ TOP_OFFSET
+ (offset
* this->resize
.step_height
), buf
, TC_BLACK
);
416 void DrawWidget (BlitArea
*dpi
, const Rect
&r
, int widget
) const OVERRIDE
419 case WID_NGRFI_VEH_CHAIN
: {
420 const Vehicle
*v
= Vehicle::Get(this->GetFeatureIndex());
424 for (const Vehicle
*u
= v
->First(); u
!= NULL
; u
= u
->Next()) {
425 if (u
== v
) sel_start
= total_width
;
427 case VEH_TRAIN
: total_width
+= Train ::From(u
)->GetDisplayImageWidth(); break;
428 case VEH_ROAD
: total_width
+= RoadVehicle::From(u
)->GetDisplayImageWidth(); break;
429 default: NOT_REACHED();
431 if (u
== v
) sel_end
= total_width
;
434 int width
= r
.right
+ 1 - r
.left
- WD_BEVEL_LEFT
- WD_BEVEL_RIGHT
;
436 if (total_width
> width
) {
437 int sel_center
= (sel_start
+ sel_end
) / 2;
438 if (sel_center
> width
/ 2) skip
= min(total_width
- width
, sel_center
- width
/ 2);
441 GrfSpecFeature f
= GetFeatureNum(this->window_number
);
442 int h
= GetVehicleImageCellSize((VehicleType
)(VEH_TRAIN
+ (f
- GSF_TRAINS
)), EIT_IN_DEPOT
).height
;
443 int y
= (r
.top
+ r
.bottom
- h
) / 2;
444 DrawVehicleImage (v
->First(), dpi
, r
.left
+ WD_BEVEL_LEFT
, r
.right
- WD_BEVEL_RIGHT
, y
+ 1, EIT_IN_DETAILS
, skip
);
446 /* Highlight the articulated part (this is different to the whole-vehicle highlighting of DrawVehicleImage */
447 if (_current_text_dir
== TD_RTL
) {
448 DrawFrameRect (dpi
, r
.right
- sel_end
+ skip
, y
, r
.right
- sel_start
+ skip
, y
+ h
, COLOUR_WHITE
, FR_BORDERONLY
);
450 DrawFrameRect (dpi
, r
.left
+ sel_start
- skip
, y
, r
.left
+ sel_end
- skip
, y
+ h
, COLOUR_WHITE
, FR_BORDERONLY
);
456 if (widget
!= WID_NGRFI_MAINPANEL
) return;
458 uint index
= this->GetFeatureIndex();
459 const NIFeature
*nif
= GetFeature(this->window_number
);
460 const NIHelper
*nih
= nif
->helper
;
461 const void *base
= nih
->GetInstance(index
);
462 const void *base_spec
= nih
->GetSpec(index
);
465 if (nif
->variables
!= NULL
) {
466 this->DrawString (dpi
, r
, i
++, "Variables:");
467 for (const NIVariable
*niv
= nif
->variables
; niv
->name
!= NULL
; niv
++) {
469 uint param
= HasVariableParameter(niv
->var
) ? NewGRFInspectWindow::var60params
[GetFeatureNum(this->window_number
)][niv
->var
- 0x60] : 0;
470 uint value
= nih
->Resolve(index
, niv
->var
, param
, &avail
);
472 if (!avail
) continue;
474 if (HasVariableParameter(niv
->var
)) {
475 this->DrawString (dpi
, r
, i
++, " %02x[%02x]: %08x (%s)", niv
->var
, param
, value
, niv
->name
);
477 this->DrawString (dpi
, r
, i
++, " %02x: %08x (%s)", niv
->var
, value
, niv
->name
);
482 uint psa_size
= nih
->GetPSASize(index
, this->caller_grfid
);
483 const int32
*psa
= nih
->GetPSAFirstPosition(index
, this->caller_grfid
);
484 if (psa_size
!= 0 && psa
!= NULL
) {
485 if (nih
->PSAWithParameter()) {
486 this->DrawString (dpi
, r
, i
++, "Persistent storage [%08X]:", BSWAP32(this->caller_grfid
));
488 this->DrawString (dpi
, r
, i
++, "Persistent storage:");
490 assert(psa_size
% 4 == 0);
491 for (uint j
= 0; j
< psa_size
; j
+= 4, psa
+= 4) {
492 this->DrawString (dpi
, r
, i
++, " %i: %i %i %i %i", j
, psa
[0], psa
[1], psa
[2], psa
[3]);
496 if (nif
->properties
!= NULL
) {
497 this->DrawString (dpi
, r
, i
++, "Properties:");
498 for (const NIProperty
*nip
= nif
->properties
; nip
->name
!= NULL
; nip
++) {
499 const void *ptr
= (const byte
*)base
+ nip
->offset
;
501 switch (nip
->read_size
) {
502 case 1: value
= *(const uint8
*)ptr
; break;
503 case 2: value
= *(const uint16
*)ptr
; break;
504 case 4: value
= *(const uint32
*)ptr
; break;
505 default: NOT_REACHED();
512 string
= STR_JUST_INT
;
516 string
= value
!= INVALID_CARGO
? CargoSpec::Get(value
)->name
: STR_QUANTITY_N_A
;
524 GetString (buffer
, string
);
525 this->DrawString (dpi
, r
, i
++, " %02x: %s (%s)", nip
->prop
, buffer
, nip
->name
);
529 if (nif
->callbacks
!= NULL
) {
530 this->DrawString (dpi
, r
, i
++, "Callbacks:");
531 for (const NICallback
*nic
= nif
->callbacks
; nic
->name
!= NULL
; nic
++) {
532 if (nic
->cb_bit
!= CBM_NO_BIT
) {
533 const void *ptr
= (const byte
*)base_spec
+ nic
->offset
;
535 switch (nic
->read_size
) {
536 case 1: value
= *(const uint8
*)ptr
; break;
537 case 2: value
= *(const uint16
*)ptr
; break;
538 case 4: value
= *(const uint32
*)ptr
; break;
539 default: NOT_REACHED();
542 if (!HasBit(value
, nic
->cb_bit
)) continue;
543 this->DrawString (dpi
, r
, i
++, " %03x: %s", nic
->cb_id
, nic
->name
);
545 this->DrawString (dpi
, r
, i
++, " %03x: %s (unmasked)", nic
->cb_id
, nic
->name
);
550 /* Not nice and certainly a hack, but it beats duplicating
551 * this whole function just to count the actual number of
552 * elements. Especially because they need to be redrawn. */
553 const_cast<NewGRFInspectWindow
*>(this)->vscroll
->SetCount(i
);
556 virtual void OnClick(Point pt
, int widget
, int click_count
)
559 case WID_NGRFI_PARENT
: {
560 const NIHelper
*nih
= GetFeatureHelper(this->window_number
);
561 uint index
= nih
->GetParent(this->GetFeatureIndex());
562 ::ShowNewGRFInspectWindow(GetFeatureNum(index
), ::GetFeatureIndex(index
), nih
->GetGRFID(this->GetFeatureIndex()));
566 case WID_NGRFI_VEH_PREV
:
567 if (this->chain_index
> 0) {
569 this->InvalidateData();
573 case WID_NGRFI_VEH_NEXT
:
574 if (this->HasChainIndex()) {
575 uint index
= this->GetFeatureIndex();
576 Vehicle
*v
= Vehicle::Get(index
);
577 if (v
!= NULL
&& v
->Next() != NULL
) {
579 this->InvalidateData();
584 case WID_NGRFI_MAINPANEL
: {
585 /* Does this feature have variables? */
586 const NIFeature
*nif
= GetFeature(this->window_number
);
587 if (nif
->variables
== NULL
) return;
589 /* Get the line, make sure it's within the boundaries. */
590 int line
= this->vscroll
->GetScrolledRowFromWidget(pt
.y
, this, WID_NGRFI_MAINPANEL
, TOP_OFFSET
);
591 if (line
== INT_MAX
) return;
593 /* Find the variable related to the line */
594 for (const NIVariable
*niv
= nif
->variables
; niv
->name
!= NULL
; niv
++, line
--) {
595 if (line
!= 1) continue; // 1 because of the "Variables:" line
597 if (!HasVariableParameter(niv
->var
)) break;
599 this->current_edit_param
= niv
->var
;
600 ShowQueryString(STR_EMPTY
, STR_NEWGRF_INSPECT_QUERY_CAPTION
, 9, this, CS_HEXADECIMAL
, QSF_NONE
);
606 virtual void OnQueryTextFinished(char *str
)
608 if (StrEmpty(str
)) return;
610 NewGRFInspectWindow::var60params
[GetFeatureNum(this->window_number
)][this->current_edit_param
- 0x60] = strtol(str
, NULL
, 16);
614 virtual void OnResize()
616 this->vscroll
->SetCapacityFromWidget(this, WID_NGRFI_MAINPANEL
, TOP_OFFSET
+ BOTTOM_OFFSET
);
620 * Some data on this window has become invalid.
621 * @param data Information about the changed data.
622 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
624 virtual void OnInvalidateData(int data
= 0, bool gui_scope
= true)
626 if (!gui_scope
) return;
627 if (this->HasChainIndex()) {
628 this->ValidateChainIndex();
629 this->SetWidgetDisabledState(WID_NGRFI_VEH_PREV
, this->chain_index
== 0);
630 Vehicle
*v
= Vehicle::Get(this->GetFeatureIndex());
631 this->SetWidgetDisabledState(WID_NGRFI_VEH_NEXT
, v
== NULL
|| v
->Next() == NULL
);
636 /* static */ uint32
NewGRFInspectWindow::var60params
[GSF_FAKE_END
][0x20] = { {0} }; // Use spec to have 0s in whole array
638 static const NWidgetPart _nested_newgrf_inspect_chain_widgets
[] = {
639 NWidget(NWID_HORIZONTAL
),
640 NWidget(WWT_CLOSEBOX
, COLOUR_GREY
),
641 NWidget(WWT_CAPTION
, COLOUR_GREY
, WID_NGRFI_CAPTION
), SetDataTip(STR_NEWGRF_INSPECT_CAPTION
, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS
),
642 NWidget(WWT_SHADEBOX
, COLOUR_GREY
),
643 NWidget(WWT_DEFSIZEBOX
, COLOUR_GREY
),
644 NWidget(WWT_STICKYBOX
, COLOUR_GREY
),
646 NWidget(WWT_PANEL
, COLOUR_GREY
),
647 NWidget(NWID_HORIZONTAL
),
648 NWidget(WWT_PUSHARROWBTN
, COLOUR_GREY
, WID_NGRFI_VEH_PREV
), SetDataTip(AWV_DECREASE
, STR_NULL
),
649 NWidget(WWT_PUSHARROWBTN
, COLOUR_GREY
, WID_NGRFI_VEH_NEXT
), SetDataTip(AWV_INCREASE
, STR_NULL
),
650 NWidget(WWT_EMPTY
, COLOUR_GREY
, WID_NGRFI_VEH_CHAIN
), SetFill(1, 0), SetResize(1, 0),
653 NWidget(NWID_HORIZONTAL
),
654 NWidget(WWT_PANEL
, COLOUR_GREY
, WID_NGRFI_MAINPANEL
), SetMinimalSize(300, 0), SetScrollbar(WID_NGRFI_SCROLLBAR
), EndContainer(),
655 NWidget(NWID_VERTICAL
),
656 NWidget(NWID_VSCROLLBAR
, COLOUR_GREY
, WID_NGRFI_SCROLLBAR
),
657 NWidget(WWT_RESIZEBOX
, COLOUR_GREY
),
662 static const NWidgetPart _nested_newgrf_inspect_widgets
[] = {
663 NWidget(NWID_HORIZONTAL
),
664 NWidget(WWT_CLOSEBOX
, COLOUR_GREY
),
665 NWidget(WWT_CAPTION
, COLOUR_GREY
, WID_NGRFI_CAPTION
), SetDataTip(STR_NEWGRF_INSPECT_CAPTION
, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS
),
666 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_NGRFI_PARENT
), SetDataTip(STR_NEWGRF_INSPECT_PARENT_BUTTON
, STR_NEWGRF_INSPECT_PARENT_TOOLTIP
),
667 NWidget(WWT_SHADEBOX
, COLOUR_GREY
),
668 NWidget(WWT_DEFSIZEBOX
, COLOUR_GREY
),
669 NWidget(WWT_STICKYBOX
, COLOUR_GREY
),
671 NWidget(NWID_HORIZONTAL
),
672 NWidget(WWT_PANEL
, COLOUR_GREY
, WID_NGRFI_MAINPANEL
), SetMinimalSize(300, 0), SetScrollbar(WID_NGRFI_SCROLLBAR
), EndContainer(),
673 NWidget(NWID_VERTICAL
),
674 NWidget(NWID_VSCROLLBAR
, COLOUR_GREY
, WID_NGRFI_SCROLLBAR
),
675 NWidget(WWT_RESIZEBOX
, COLOUR_GREY
),
680 static WindowDesc::Prefs
_newgrf_inspect_chain_prefs ("newgrf_inspect_chain");
682 static const WindowDesc
_newgrf_inspect_chain_desc(
684 WC_NEWGRF_INSPECT
, WC_NONE
,
686 _nested_newgrf_inspect_chain_widgets
, lengthof(_nested_newgrf_inspect_chain_widgets
),
687 &_newgrf_inspect_chain_prefs
690 static WindowDesc::Prefs
_newgrf_inspect_prefs ("newgrf_inspect");
692 static const WindowDesc
_newgrf_inspect_desc(
694 WC_NEWGRF_INSPECT
, WC_NONE
,
696 _nested_newgrf_inspect_widgets
, lengthof(_nested_newgrf_inspect_widgets
),
697 &_newgrf_inspect_prefs
701 * Show the inspect window for a given feature and index.
702 * The index is normally an in-game location/identifier, such
703 * as a TileIndex or an IndustryID depending on the feature
704 * we want to inspect.
705 * @param feature The feature we want to inspect.
706 * @param index The index/identifier of the feature to inspect.
707 * @param grfid GRFID of the item opening this window, or 0 if not opened by other window.
709 void ShowNewGRFInspectWindow(GrfSpecFeature feature
, uint index
, const uint32 grfid
)
711 if (!IsNewGRFInspectable(feature
, index
)) return;
713 WindowNumber wno
= GetInspectWindowNumber(feature
, index
);
714 const WindowDesc
*desc
= (feature
== GSF_TRAINS
|| feature
== GSF_ROADVEHICLES
) ? &_newgrf_inspect_chain_desc
: &_newgrf_inspect_desc
;
715 NewGRFInspectWindow
*w
= AllocateWindowDescFront
<NewGRFInspectWindow
>(desc
, wno
, true);
716 w
->SetCallerGRFID(grfid
);
720 * Invalidate the inspect window for a given feature and index.
721 * The index is normally an in-game location/identifier, such
722 * as a TileIndex or an IndustryID depending on the feature
723 * we want to inspect.
724 * @param feature The feature we want to invalidate the window for.
725 * @param index The index/identifier of the feature to invalidate.
727 void InvalidateNewGRFInspectWindow(GrfSpecFeature feature
, uint index
)
729 if (feature
== GSF_INVALID
) return;
731 WindowNumber wno
= GetInspectWindowNumber(feature
, index
);
732 InvalidateWindowData(WC_NEWGRF_INSPECT
, wno
);
736 * Delete inspect window for a given feature and index.
737 * The index is normally an in-game location/identifier, such
738 * as a TileIndex or an IndustryID depending on the feature
739 * we want to inspect.
740 * @param feature The feature we want to delete the window for.
741 * @param index The index/identifier of the feature to delete.
743 void DeleteNewGRFInspectWindow(GrfSpecFeature feature
, uint index
)
745 if (feature
== GSF_INVALID
) return;
747 WindowNumber wno
= GetInspectWindowNumber(feature
, index
);
748 DeleteWindowById(WC_NEWGRF_INSPECT
, wno
);
750 /* Reinitialise the land information window to remove the "debug" sprite if needed.
751 * Note: Since we might be called from a command here, it is important to not execute
752 * the invalidation immediately. The landinfo window tests commands itself. */
753 InvalidateWindowData(WC_LAND_INFO
, 0, 1);
757 * Can we inspect the data given a certain feature and index.
758 * The index is normally an in-game location/identifier, such
759 * as a TileIndex or an IndustryID depending on the feature
760 * we want to inspect.
761 * @param feature The feature we want to inspect.
762 * @param index The index/identifier of the feature to inspect.
763 * @return true if there is something to show.
765 bool IsNewGRFInspectable(GrfSpecFeature feature
, uint index
)
767 const NIFeature
*nif
= GetFeature(GetInspectWindowNumber(feature
, index
));
768 if (nif
== NULL
) return false;
769 return nif
->helper
->IsInspectable(index
);
773 * Get the GrfSpecFeature associated with the tile.
774 * @param tile The tile to get the feature from.
775 * @return the GrfSpecFeature.
777 GrfSpecFeature
GetGrfSpecFeature(TileIndex tile
)
779 if (IsHouseTile(tile
)) {
781 } else if (IsIndustryTile(tile
)) {
782 return GSF_INDUSTRYTILES
;
785 switch (GetTileType(tile
)) {
786 default: return GSF_INVALID
;
787 case TT_RAILWAY
: return IsTileSubtype(tile
, TT_TRACK
) ? GSF_RAILTYPES
: GSF_INVALID
;
788 case TT_MISC
: return (IsRailDepotTile(tile
) || IsLevelCrossingTile(tile
)) ? GSF_RAILTYPES
: GSF_INVALID
;
789 case TT_OBJECT
: return GSF_OBJECTS
;
792 switch (GetStationType(tile
)) {
793 case STATION_RAIL
: return GSF_STATIONS
;
794 case STATION_AIRPORT
: return GSF_AIRPORTTILES
;
795 default: return GSF_INVALID
;
801 * Get the GrfSpecFeature associated with the vehicle.
802 * @param type The vehicle type to get the feature from.
803 * @return the GrfSpecFeature.
805 GrfSpecFeature
GetGrfSpecFeature(VehicleType type
)
808 case VEH_TRAIN
: return GSF_TRAINS
;
809 case VEH_ROAD
: return GSF_ROADVEHICLES
;
810 case VEH_SHIP
: return GSF_SHIPS
;
811 case VEH_AIRCRAFT
: return GSF_AIRCRAFT
;
812 default: return GSF_INVALID
;
818 /**** Sprite Aligner ****/
820 /** Window used for aligning sprites. */
821 struct SpriteAlignerWindow
: Window
{
822 typedef SmallPair
<int16
, int16
> XyOffs
; ///< Pair for x and y offsets of the sprite before alignment. First value contains the x offset, second value y offset.
824 SpriteID current_sprite
; ///< The currently shown sprite.
826 SmallMap
<SpriteID
, XyOffs
> offs_start_map
; ///< Mapping of starting offsets for the sprites which have been aligned in the sprite aligner window.
828 SpriteAlignerWindow (const WindowDesc
*desc
, WindowNumber wno
) :
829 Window (desc
), current_sprite (0), vscroll (NULL
)
831 this->CreateNestedTree();
832 this->vscroll
= this->GetScrollbar(WID_SA_SCROLLBAR
);
833 this->InitNested(wno
);
835 /* Oh yes, we assume there is at least one normal sprite! */
836 while (!IsNormalSprite (this->current_sprite
)) this->current_sprite
++;
839 virtual void SetStringParameters(int widget
) const
841 const Sprite
*spr
= GetSprite(this->current_sprite
, ST_NORMAL
);
844 SetDParam(0, this->current_sprite
);
845 SetDParamStr(1, FioGetFilename(GetOriginFileSlot(this->current_sprite
)));
848 case WID_SA_OFFSETS_ABS
:
849 SetDParam(0, spr
->x_offs
);
850 SetDParam(1, spr
->y_offs
);
853 case WID_SA_OFFSETS_REL
: {
854 /* Relative offset is new absolute offset - starting absolute offset.
855 * Show 0, 0 as the relative offsets if entry is not in the map (meaning they have not been changed yet).
857 const SmallPair
<SpriteID
, XyOffs
> *key_offs_pair
= this->offs_start_map
.Find(this->current_sprite
);
858 if (key_offs_pair
!= this->offs_start_map
.End()) {
859 SetDParam(0, spr
->x_offs
- key_offs_pair
->second
.first
);
860 SetDParam(1, spr
->y_offs
- key_offs_pair
->second
.second
);
873 virtual void UpdateWidgetSize(int widget
, Dimension
*size
, const Dimension
&padding
, Dimension
*fill
, Dimension
*resize
)
875 if (widget
!= WID_SA_LIST
) return;
877 resize
->height
= max(11, FONT_HEIGHT_NORMAL
+ 1);
880 /* Resize to about 200 pixels (for the preview) */
881 size
->height
= (1 + 200 / resize
->height
) * resize
->height
;
884 void DrawWidget (BlitArea
*dpi
, const Rect
&r
, int widget
) const OVERRIDE
887 case WID_SA_SPRITE
: {
888 /* Center the sprite ourselves */
889 const Sprite
*spr
= GetSprite(this->current_sprite
, ST_NORMAL
);
890 int width
= r
.right
- r
.left
+ 1 - WD_BEVEL_LEFT
- WD_BEVEL_RIGHT
;
891 int height
= r
.bottom
- r
.top
+ 1 - WD_BEVEL_TOP
- WD_BEVEL_BOTTOM
;
892 int x
= -UnScaleGUI(spr
->x_offs
) + (width
- UnScaleGUI(spr
->width
) ) / 2;
893 int y
= -UnScaleGUI(spr
->y_offs
) + (height
- UnScaleGUI(spr
->height
)) / 2;
896 if (!InitBlitArea (dpi
, &new_dpi
, r
.left
+ WD_BEVEL_LEFT
, r
.top
+ WD_BEVEL_TOP
, width
, height
)) break;
898 DrawSprite (&new_dpi
, this->current_sprite
, PAL_NONE
, x
, y
);
904 const NWidgetBase
*nwid
= this->GetWidget
<NWidgetBase
>(widget
);
905 int step_size
= nwid
->resize_y
;
907 SmallVector
<SpriteID
, 256> &list
= _newgrf_debug_sprite_picker
.sprites
;
908 int max
= min
<int>(this->vscroll
->GetPosition() + this->vscroll
->GetCapacity(), list
.Length());
910 int y
= r
.top
+ WD_FRAMERECT_TOP
;
911 for (int i
= this->vscroll
->GetPosition(); i
< max
; i
++) {
912 SetDParam(0, list
[i
]);
913 DrawString (dpi
, r
.left
+ WD_FRAMERECT_LEFT
, r
.right
- WD_FRAMERECT_RIGHT
, y
, STR_BLACK_COMMA
, TC_FROMSTRING
, SA_RIGHT
| SA_FORCE
);
921 virtual void OnClick(Point pt
, int widget
, int click_count
)
924 case WID_SA_PREVIOUS
:
926 this->current_sprite
= (this->current_sprite
== 0 ? GetMaxSpriteID() : this->current_sprite
) - 1;
927 } while (!IsNormalSprite (this->current_sprite
));
932 ShowQueryString(STR_EMPTY
, STR_SPRITE_ALIGNER_GOTO_CAPTION
, 7, this, CS_NUMERAL
, QSF_NONE
);
937 this->current_sprite
= (this->current_sprite
+ 1) % GetMaxSpriteID();
938 } while (!IsNormalSprite (this->current_sprite
));
943 this->LowerWidget(WID_SA_PICKER
);
944 _newgrf_debug_sprite_picker
.mode
= SPM_WAIT_CLICK
;
949 const NWidgetBase
*nwid
= this->GetWidget
<NWidgetBase
>(widget
);
950 int step_size
= nwid
->resize_y
;
952 uint i
= this->vscroll
->GetPosition() + (pt
.y
- nwid
->pos_y
) / step_size
;
953 if (i
< _newgrf_debug_sprite_picker
.sprites
.Length()) {
954 SpriteID spr
= _newgrf_debug_sprite_picker
.sprites
[i
];
955 if (IsNormalSprite (spr
)) this->current_sprite
= spr
;
966 * Yes... this is a hack.
968 * No... I don't think it is useful to make this less of a hack.
970 * If you want to align sprites, you just need the number. Generally
971 * the sprite caches are big enough to not remove the sprite from the
972 * cache. If that's not the case, just let the NewGRF developer
973 * increase the cache size instead of storing thousands of offsets
974 * for the incredibly small chance that it's actually going to be
975 * used by someone and the sprite cache isn't big enough for that
976 * particular NewGRF developer.
978 Sprite
*spr
= const_cast<Sprite
*>(GetSprite(this->current_sprite
, ST_NORMAL
));
980 /* Remember the original offsets of the current sprite, if not already in mapping. */
981 if (!(this->offs_start_map
.Contains(this->current_sprite
))) {
982 this->offs_start_map
.Insert(this->current_sprite
, XyOffs(spr
->x_offs
, spr
->y_offs
));
985 /* Move eight units at a time if ctrl is pressed. */
986 case WID_SA_UP
: spr
->y_offs
-= _ctrl_pressed
? 8 : 1; break;
987 case WID_SA_DOWN
: spr
->y_offs
+= _ctrl_pressed
? 8 : 1; break;
988 case WID_SA_LEFT
: spr
->x_offs
-= _ctrl_pressed
? 8 : 1; break;
989 case WID_SA_RIGHT
: spr
->x_offs
+= _ctrl_pressed
? 8 : 1; break;
991 /* Of course, we need to redraw the sprite, but where is it used?
992 * Everywhere is a safe bet. */
993 MarkWholeScreenDirty();
997 case WID_SA_RESET_REL
:
998 /* Reset the starting offsets for the current sprite. */
999 this->offs_start_map
.Erase(this->current_sprite
);
1005 virtual void OnQueryTextFinished(char *str
)
1007 if (StrEmpty(str
)) return;
1009 this->current_sprite
= atoi(str
);
1010 if (this->current_sprite
>= GetMaxSpriteID()) this->current_sprite
= 0;
1011 while (!IsNormalSprite (this->current_sprite
)) {
1012 this->current_sprite
= (this->current_sprite
+ 1) % GetMaxSpriteID();
1018 * Some data on this window has become invalid.
1019 * @param data Information about the changed data.
1020 * @param gui_scope Whether the call is done from GUI scope. You may not do everything when not in GUI scope. See #InvalidateWindowData() for details.
1022 virtual void OnInvalidateData(int data
= 0, bool gui_scope
= true)
1024 if (!gui_scope
) return;
1026 /* Sprite picker finished */
1027 this->RaiseWidget(WID_SA_PICKER
);
1028 this->vscroll
->SetCount(_newgrf_debug_sprite_picker
.sprites
.Length());
1032 virtual void OnResize()
1034 this->vscroll
->SetCapacityFromWidget(this, WID_SA_LIST
);
1038 static const NWidgetPart _nested_sprite_aligner_widgets
[] = {
1039 NWidget(NWID_HORIZONTAL
),
1040 NWidget(WWT_CLOSEBOX
, COLOUR_GREY
),
1041 NWidget(WWT_CAPTION
, COLOUR_GREY
, WID_SA_CAPTION
), SetDataTip(STR_SPRITE_ALIGNER_CAPTION
, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS
),
1042 NWidget(WWT_SHADEBOX
, COLOUR_GREY
),
1043 NWidget(WWT_STICKYBOX
, COLOUR_GREY
),
1045 NWidget(WWT_PANEL
, COLOUR_GREY
),
1046 NWidget(NWID_HORIZONTAL
), SetPIP(0, 0, 10),
1047 NWidget(NWID_VERTICAL
), SetPIP(10, 5, 10),
1048 NWidget(NWID_HORIZONTAL
, NC_EQUALSIZE
), SetPIP(10, 5, 10),
1049 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_SA_PREVIOUS
), SetDataTip(STR_SPRITE_ALIGNER_PREVIOUS_BUTTON
, STR_SPRITE_ALIGNER_PREVIOUS_TOOLTIP
), SetFill(1, 0),
1050 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_SA_GOTO
), SetDataTip(STR_SPRITE_ALIGNER_GOTO_BUTTON
, STR_SPRITE_ALIGNER_GOTO_TOOLTIP
), SetFill(1, 0),
1051 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_SA_NEXT
), SetDataTip(STR_SPRITE_ALIGNER_NEXT_BUTTON
, STR_SPRITE_ALIGNER_NEXT_TOOLTIP
), SetFill(1, 0),
1053 NWidget(NWID_HORIZONTAL
), SetPIP(10, 5, 10),
1054 NWidget(NWID_SPACER
), SetFill(1, 1),
1055 NWidget(WWT_PUSHIMGBTN
, COLOUR_GREY
, WID_SA_UP
), SetDataTip(SPR_ARROW_UP
, STR_SPRITE_ALIGNER_MOVE_TOOLTIP
), SetResize(0, 0),
1056 NWidget(NWID_SPACER
), SetFill(1, 1),
1058 NWidget(NWID_HORIZONTAL_LTR
), SetPIP(10, 5, 10),
1059 NWidget(NWID_VERTICAL
),
1060 NWidget(NWID_SPACER
), SetFill(1, 1),
1061 NWidget(WWT_PUSHIMGBTN
, COLOUR_GREY
, WID_SA_LEFT
), SetDataTip(SPR_ARROW_LEFT
, STR_SPRITE_ALIGNER_MOVE_TOOLTIP
), SetResize(0, 0),
1062 NWidget(NWID_SPACER
), SetFill(1, 1),
1064 NWidget(WWT_PANEL
, COLOUR_DARK_BLUE
, WID_SA_SPRITE
), SetDataTip(STR_NULL
, STR_SPRITE_ALIGNER_SPRITE_TOOLTIP
),
1066 NWidget(NWID_VERTICAL
),
1067 NWidget(NWID_SPACER
), SetFill(1, 1),
1068 NWidget(WWT_PUSHIMGBTN
, COLOUR_GREY
, WID_SA_RIGHT
), SetDataTip(SPR_ARROW_RIGHT
, STR_SPRITE_ALIGNER_MOVE_TOOLTIP
), SetResize(0, 0),
1069 NWidget(NWID_SPACER
), SetFill(1, 1),
1072 NWidget(NWID_HORIZONTAL
), SetPIP(10, 5, 10),
1073 NWidget(NWID_SPACER
), SetFill(1, 1),
1074 NWidget(WWT_PUSHIMGBTN
, COLOUR_GREY
, WID_SA_DOWN
), SetDataTip(SPR_ARROW_DOWN
, STR_SPRITE_ALIGNER_MOVE_TOOLTIP
), SetResize(0, 0),
1075 NWidget(NWID_SPACER
), SetFill(1, 1),
1077 NWidget(WWT_LABEL
, COLOUR_GREY
, WID_SA_OFFSETS_ABS
), SetDataTip(STR_SPRITE_ALIGNER_OFFSETS_ABS
, STR_NULL
), SetFill(1, 0), SetPadding(0, 10, 0, 10),
1078 NWidget(WWT_LABEL
, COLOUR_GREY
, WID_SA_OFFSETS_REL
), SetDataTip(STR_SPRITE_ALIGNER_OFFSETS_REL
, STR_NULL
), SetFill(1, 0), SetPadding(0, 10, 0, 10),
1079 NWidget(NWID_HORIZONTAL
), SetPIP(10, 5, 10),
1080 NWidget(NWID_SPACER
), SetFill(1, 1),
1081 NWidget(WWT_PUSHTXTBTN
, COLOUR_GREY
, WID_SA_RESET_REL
), SetDataTip(STR_SPRITE_ALIGNER_RESET_BUTTON
, STR_SPRITE_ALIGNER_RESET_TOOLTIP
), SetFill(0, 0),
1082 NWidget(NWID_SPACER
), SetFill(1, 1),
1085 NWidget(NWID_VERTICAL
), SetPIP(10, 5, 10),
1086 NWidget(WWT_TEXTBTN
, COLOUR_GREY
, WID_SA_PICKER
), SetDataTip(STR_SPRITE_ALIGNER_PICKER_BUTTON
, STR_SPRITE_ALIGNER_PICKER_TOOLTIP
), SetFill(1, 0),
1087 NWidget(NWID_HORIZONTAL
),
1088 NWidget(WWT_MATRIX
, COLOUR_GREY
, WID_SA_LIST
), SetResize(1, 1), SetMatrixDataTip(1, 0, STR_NULL
), SetFill(1, 1), SetScrollbar(WID_SA_SCROLLBAR
),
1089 NWidget(NWID_VSCROLLBAR
, COLOUR_GREY
, WID_SA_SCROLLBAR
),
1096 static WindowDesc::Prefs
_sprite_aligner_prefs ("sprite_aligner");
1098 static const WindowDesc
_sprite_aligner_desc(
1100 WC_SPRITE_ALIGNER
, WC_NONE
,
1102 _nested_sprite_aligner_widgets
, lengthof(_nested_sprite_aligner_widgets
),
1103 &_sprite_aligner_prefs
1107 * Show the window for aligning sprites.
1109 void ShowSpriteAlignerWindow()
1111 AllocateWindowDescFront
<SpriteAlignerWindow
>(&_sprite_aligner_desc
, 0);