diff --git a/src/console_cmds.cpp b/src/console_cmds.cpp index 750fa768d3..a23f8c2172 100644 --- a/src/console_cmds.cpp +++ b/src/console_cmds.cpp @@ -3056,6 +3056,7 @@ DEF_CONSOLE_CMD(ConMiscDebug) IConsoleHelp(" 2: MDF_ZONING_RS_WATER_FLOOD_STATE"); IConsoleHelp(" 4: MDF_ZONING_RS_TROPIC_ZONE"); IConsoleHelp(" 8: MDF_ZONING_RS_ANIMATED_TILE"); + IConsoleHelp(" 10: MDF_NEWGRF_SG_SAVE_RAW"); return true; } diff --git a/src/debug_settings.h b/src/debug_settings.h index 209ff2e490..4ee244e6f5 100644 --- a/src/debug_settings.h +++ b/src/debug_settings.h @@ -45,6 +45,7 @@ enum MiscDebugFlags { MDF_ZONING_RS_WATER_FLOOD_STATE, MDF_ZONING_RS_TROPIC_ZONE, MDF_ZONING_RS_ANIMATED_TILE, + MDF_NEWGRF_SG_SAVE_RAW, }; extern uint32 _misc_debug_flags; diff --git a/src/lang/english.txt b/src/lang/english.txt index 92ff143261..2b6b0ec88e 100644 --- a/src/lang/english.txt +++ b/src/lang/english.txt @@ -4216,6 +4216,8 @@ STR_NEWGRF_INSPECT_DUPLICATE :{BLACK}D STR_NEWGRF_INSPECT_DUPLICATE_TOOLTIP :{BLACK}Duplicate this window STR_NEWGRF_INSPECT_SPRITE_DUMP :{BLACK}S STR_NEWGRF_INSPECT_SPRITE_DUMP_TOOLTIP :{BLACK}Display current sprite chain +STR_NEWGRF_INSPECT_SPRITE_DUMP_UNOPT :{BLACK}U +STR_NEWGRF_INSPECT_SPRITE_DUMP_UNOPT_TOOLTIP :{BLACK}Display sprite groups without any optimisations applied.{}{}Requires reloading NewGRFs if not previously enabled (misc_debug 10). STR_NEWGRF_INSPECT_SPRITE_DUMP_PANEL_TOOLTIP :{BLACK}Click to highlight sprite group{}Shift+Click to collapse sprite group{}Ctrl+Click to highlight temporary storage register STR_NEWGRF_INSPECT_CAPTION_OBJECT_AT :{STRING1} at {HEX} diff --git a/src/newgrf.cpp b/src/newgrf.cpp index a6ea96a0b3..6ccae4aeda 100644 --- a/src/newgrf.cpp +++ b/src/newgrf.cpp @@ -5544,20 +5544,8 @@ static const CallbackResultSpriteGroup *NewCallbackResultSpriteGroup(uint16 grou return ptr; } -/* Helper function to either create a callback or link to a previously - * defined spritegroup. */ -static const SpriteGroup *GetGroupFromGroupID(byte setid, byte type, uint16 groupid) +static const SpriteGroup *PruneTargetSpriteGroup(const SpriteGroup *result) { - if (HasBit(groupid, 15)) { - return NewCallbackResultSpriteGroup(groupid); - } - - if (groupid > MAX_SPRITEGROUP || _cur.spritegroups[groupid] == nullptr) { - grfmsg(1, "GetGroupFromGroupID(0x%02X:0x%02X): Groupid 0x%04X does not exist, leaving empty", setid, type, groupid); - return nullptr; - } - - const SpriteGroup *result = _cur.spritegroups[groupid]; if (HasGrfOptimiserFlag(NGOF_NO_OPT_VARACT2) || HasGrfOptimiserFlag(NGOF_NO_OPT_VARACT2_GROUP_PRUNE)) return result; while (result != nullptr) { if (result->type == SGT_DETERMINISTIC) { @@ -5580,6 +5568,24 @@ static const SpriteGroup *GetGroupFromGroupID(byte setid, byte type, uint16 grou return result; } +/* Helper function to either create a callback or link to a previously + * defined spritegroup. */ +static const SpriteGroup *GetGroupFromGroupID(byte setid, byte type, uint16 groupid) +{ + if (HasBit(groupid, 15)) { + return NewCallbackResultSpriteGroup(groupid); + } + + if (groupid > MAX_SPRITEGROUP || _cur.spritegroups[groupid] == nullptr) { + grfmsg(1, "GetGroupFromGroupID(0x%02X:0x%02X): Groupid 0x%04X does not exist, leaving empty", setid, type, groupid); + return nullptr; + } + + const SpriteGroup *result = _cur.spritegroups[groupid]; + if (likely(!HasBit(_misc_debug_flags, MDF_NEWGRF_SG_SAVE_RAW))) result = PruneTargetSpriteGroup(result); + return result; +} + static const SpriteGroup *GetGroupByID(uint16 groupid) { const SpriteGroup *result = _cur.spritegroups[groupid]; @@ -6793,6 +6799,46 @@ static void HandleVarAction2OptimisationPasses() } } +static void ProcessDeterministicSpriteGroupRanges(const std::vector &ranges, std::vector &ranges_out, const SpriteGroup *default_group) +{ + /* Sort ranges ascending. When ranges overlap, this may required clamping or splitting them */ + std::vector bounds; + for (uint i = 0; i < ranges.size(); i++) { + bounds.push_back(ranges[i].low); + if (ranges[i].high != UINT32_MAX) bounds.push_back(ranges[i].high + 1); + } + std::sort(bounds.begin(), bounds.end()); + bounds.erase(std::unique(bounds.begin(), bounds.end()), bounds.end()); + + std::vector target; + for (uint j = 0; j < bounds.size(); ++j) { + uint32 v = bounds[j]; + const SpriteGroup *t = default_group; + for (uint i = 0; i < ranges.size(); i++) { + if (ranges[i].low <= v && v <= ranges[i].high) { + t = ranges[i].group; + break; + } + } + target.push_back(t); + } + assert(target.size() == bounds.size()); + + for (uint j = 0; j < bounds.size(); ) { + if (target[j] != default_group) { + DeterministicSpriteGroupRange &r = ranges_out.emplace_back(); + r.group = target[j]; + r.low = bounds[j]; + while (j < bounds.size() && target[j] == r.group) { + j++; + } + r.high = j < bounds.size() ? bounds[j] - 1 : UINT32_MAX; + } else { + j++; + } + } +} + /* Action 0x02 */ static void NewSpriteGroup(ByteReader *buf) { @@ -6849,6 +6895,11 @@ static void NewSpriteGroup(ByteReader *buf) case 2: group->size = DSG_SIZE_DWORD; varsize = 4; break; } + DeterministicSpriteGroupShadowCopy *shadow = nullptr; + if (unlikely(HasBit(_misc_debug_flags, MDF_NEWGRF_SG_SAVE_RAW))) { + shadow = &(_deterministic_sg_shadows[group]); + } + VarAction2OptimiseState va2_opt_state; /* The initial value is always the constant 0 */ va2_opt_state.inference = VA2AIF_SIGNED_NON_NEGATIVE | VA2AIF_ONE_OR_ZERO | VA2AIF_HAVE_CONSTANT; @@ -6904,6 +6955,11 @@ static void NewSpriteGroup(ByteReader *buf) adjust.add_val = 0; adjust.divmod_val = 0; } + if (unlikely(shadow != nullptr)) { + shadow->adjusts.push_back(adjust); + /* Pruning was turned off so that the unpruned target could be saved in the shadow, prune now */ + if (adjust.subroutine != nullptr) adjust.subroutine = PruneTargetSpriteGroup(adjust.subroutine); + } OptimiseVarAction2Adjust(va2_opt_state, feature, varsize, group, adjust); @@ -6919,47 +6975,24 @@ static void NewSpriteGroup(ByteReader *buf) } group->default_group = GetGroupFromGroupID(setid, type, buf->ReadWord()); - group->error_group = ranges.size() > 0 ? ranges[0].group : group->default_group; - /* nvar == 0 is a special case -- we turn our value into a callback result */ - group->calculated_result = ranges.size() == 0; - /* Sort ranges ascending. When ranges overlap, this may required clamping or splitting them */ - std::vector bounds; - for (uint i = 0; i < ranges.size(); i++) { - bounds.push_back(ranges[i].low); - if (ranges[i].high != UINT32_MAX) bounds.push_back(ranges[i].high + 1); - } - std::sort(bounds.begin(), bounds.end()); - bounds.erase(std::unique(bounds.begin(), bounds.end()), bounds.end()); - - std::vector target; - for (uint j = 0; j < bounds.size(); ++j) { - uint32 v = bounds[j]; - const SpriteGroup *t = group->default_group; - for (uint i = 0; i < ranges.size(); i++) { - if (ranges[i].low <= v && v <= ranges[i].high) { - t = ranges[i].group; - break; - } - } - target.push_back(t); - } - assert(target.size() == bounds.size()); + if (unlikely(shadow != nullptr)) { + ProcessDeterministicSpriteGroupRanges(ranges, shadow->ranges, group->default_group); + shadow->default_group = group->default_group; - for (uint j = 0; j < bounds.size(); ) { - if (target[j] != group->default_group) { - DeterministicSpriteGroupRange &r = group->ranges.emplace_back(); - r.group = target[j]; - r.low = bounds[j]; - while (j < bounds.size() && target[j] == r.group) { - j++; - } - r.high = j < bounds.size() ? bounds[j] - 1 : UINT32_MAX; - } else { - j++; + /* Pruning was turned off so that the unpruned targets could be saved in the shadow ranges, prune now */ + for (DeterministicSpriteGroupRange &range : ranges) { + range.group = PruneTargetSpriteGroup(range.group); } + group->default_group = PruneTargetSpriteGroup(group->default_group); } + group->error_group = ranges.size() > 0 ? ranges[0].group : group->default_group; + /* nvar == 0 is a special case -- we turn our value into a callback result */ + group->calculated_result = ranges.size() == 0; + + ProcessDeterministicSpriteGroupRanges(ranges, group->ranges, group->default_group); + OptimiseVarAction2DeterministicSpriteGroup(va2_opt_state, feature, varsize, group); break; } @@ -6994,6 +7027,16 @@ static void NewSpriteGroup(ByteReader *buf) group->groups.push_back(GetGroupFromGroupID(setid, type, buf->ReadWord())); } + if (unlikely(HasBit(_misc_debug_flags, MDF_NEWGRF_SG_SAVE_RAW))) { + RandomizedSpriteGroupShadowCopy *shadow = &(_randomized_sg_shadows[group]); + shadow->groups = group->groups; + + /* Pruning was turned off so that the unpruned targets could be saved in the shadow groups, prune now */ + for (const SpriteGroup *&group : group->groups) { + group = PruneTargetSpriteGroup(group); + } + } + break; } @@ -11289,6 +11332,9 @@ void ResetNewGRFData() InitializeSoundPool(); _spritegroup_pool.CleanPool(); _callback_result_cache.clear(); + _deterministic_sg_shadows.clear(); + _randomized_sg_shadows.clear(); + _grfs_loaded_with_sg_shadow_enable = HasBit(_misc_debug_flags, MDF_NEWGRF_SG_SAVE_RAW); } /** diff --git a/src/newgrf_debug_gui.cpp b/src/newgrf_debug_gui.cpp index 8b09ad455e..2f260ac85f 100644 --- a/src/newgrf_debug_gui.cpp +++ b/src/newgrf_debug_gui.cpp @@ -20,6 +20,7 @@ #include "vehicle_gui.h" #include "zoom_func.h" #include "scope.h" +#include "debug_settings.h" #include "engine_base.h" #include "industry.h" @@ -317,6 +318,7 @@ struct NewGRFInspectWindow : Window { bool auto_refresh = false; bool log_console = false; bool sprite_dump = false; + bool sprite_dump_unopt = false; uint32 extra_info_flags = 0; btree::btree_map extra_info_click_flag_toggles; @@ -389,7 +391,10 @@ struct NewGRFInspectWindow : Window { { this->CreateNestedTree(); this->vscroll = this->GetScrollbar(WID_NGRFI_SCROLLBAR); - this->GetWidget(WID_NGRFI_SPRITE_DUMP_SEL)->SetDisplayedPlane(GetFeatureHelper(wno)->ShowSpriteDumpButton(::GetFeatureIndex(wno)) ? 0 : SZSP_NONE); + bool show_sprite_dump_button = GetFeatureHelper(wno)->ShowSpriteDumpButton(::GetFeatureIndex(wno)); + this->GetWidget(WID_NGRFI_SPRITE_DUMP_SEL)->SetDisplayedPlane(show_sprite_dump_button ? 0 : SZSP_NONE); + this->GetWidget(WID_NGRFI_SPRITE_DUMP_UNOPT_SEL)->SetDisplayedPlane(show_sprite_dump_button ? 0 : SZSP_NONE); + this->SetWidgetDisabledState(WID_NGRFI_SPRITE_DUMP_UNOPT, true); this->FinishInitNested(wno); this->vscroll->SetCount(0); @@ -535,6 +540,7 @@ struct NewGRFInspectWindow : Window { }; const_cast(this)->sprite_group_lines.clear(); if (this->sprite_dump) { + SpriteGroupDumper::use_shadows = this->sprite_dump_unopt; bool collapsed = false; const SpriteGroup *collapse_group = nullptr; uint collapse_lines = 0; @@ -572,6 +578,7 @@ struct NewGRFInspectWindow : Window { if (highlight_tag != 0 && this->selected_highlight_tag == highlight_tag) colour = TC_YELLOW; ::DrawString(r.left + LEFT_OFFSET, r.right - RIGHT_OFFSET, r.top + TOP_OFFSET + (scroll_offset * this->resize.step_height), buf, colour); }); + SpriteGroupDumper::use_shadows = false; return; } else { NewGRFInspectWindow *this_mutable = const_cast(this); @@ -731,6 +738,20 @@ struct NewGRFInspectWindow : Window { } } + bool UnOptimisedSpriteDumpOK() const + { + if (_grfs_loaded_with_sg_shadow_enable) return true; + + if (_networking && !_network_server) return false; + + extern uint NetworkClientCount(); + if (_networking && NetworkClientCount() > 1) { + return false; + } + + return true; + } + void OnClick(Point pt, int widget, int click_count) override { switch (widget) { @@ -847,8 +868,34 @@ struct NewGRFInspectWindow : Window { case WID_NGRFI_SPRITE_DUMP: { this->sprite_dump = !this->sprite_dump; this->SetWidgetLoweredState(WID_NGRFI_SPRITE_DUMP, this->sprite_dump); + this->SetWidgetDisabledState(WID_NGRFI_SPRITE_DUMP_UNOPT, !this->sprite_dump || !UnOptimisedSpriteDumpOK()); this->GetWidget(WID_NGRFI_MAINPANEL)->SetToolTip(this->sprite_dump ? STR_NEWGRF_INSPECT_SPRITE_DUMP_PANEL_TOOLTIP : STR_NULL); this->SetWidgetDirty(WID_NGRFI_SPRITE_DUMP); + this->SetWidgetDirty(WID_NGRFI_SPRITE_DUMP_UNOPT); + this->SetWidgetDirty(WID_NGRFI_MAINPANEL); + this->SetWidgetDirty(WID_NGRFI_SCROLLBAR); + break; + } + + case WID_NGRFI_SPRITE_DUMP_UNOPT: { + if (!this->sprite_dump_unopt) { + if (!UnOptimisedSpriteDumpOK()) { + this->SetWidgetDisabledState(WID_NGRFI_SPRITE_DUMP_UNOPT, true); + this->SetWidgetDirty(WID_NGRFI_SPRITE_DUMP_UNOPT); + return; + } + if (!_grfs_loaded_with_sg_shadow_enable) { + SetBit(_misc_debug_flags, MDF_NEWGRF_SG_SAVE_RAW); + + ReloadNewGRFData(); + + extern void PostCheckNewGRFLoadWarnings(); + PostCheckNewGRFLoadWarnings(); + } + } + this->sprite_dump_unopt = !this->sprite_dump_unopt; + this->SetWidgetLoweredState(WID_NGRFI_SPRITE_DUMP_UNOPT, this->sprite_dump_unopt); + this->SetWidgetDirty(WID_NGRFI_SPRITE_DUMP_UNOPT); this->SetWidgetDirty(WID_NGRFI_MAINPANEL); this->SetWidgetDirty(WID_NGRFI_SCROLLBAR); break; @@ -904,6 +951,9 @@ static const NWidgetPart _nested_newgrf_inspect_chain_widgets[] = { NWidget(NWID_HORIZONTAL), NWidget(WWT_CLOSEBOX, COLOUR_GREY), NWidget(WWT_CAPTION, COLOUR_GREY, WID_NGRFI_CAPTION), SetDataTip(STR_NEWGRF_INSPECT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NGRFI_SPRITE_DUMP_UNOPT_SEL), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_NGRFI_SPRITE_DUMP_UNOPT), SetDataTip(STR_NEWGRF_INSPECT_SPRITE_DUMP_UNOPT, STR_NEWGRF_INSPECT_SPRITE_DUMP_UNOPT_TOOLTIP), + EndContainer(), NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NGRFI_SPRITE_DUMP_SEL), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_NGRFI_SPRITE_DUMP), SetDataTip(STR_NEWGRF_INSPECT_SPRITE_DUMP, STR_NEWGRF_INSPECT_SPRITE_DUMP_TOOLTIP), EndContainer(), @@ -935,6 +985,9 @@ static const NWidgetPart _nested_newgrf_inspect_widgets[] = { NWidget(WWT_CLOSEBOX, COLOUR_GREY), NWidget(WWT_CAPTION, COLOUR_GREY, WID_NGRFI_CAPTION), SetDataTip(STR_NEWGRF_INSPECT_CAPTION, STR_TOOLTIP_WINDOW_TITLE_DRAG_THIS), NWidget(WWT_PUSHTXTBTN, COLOUR_GREY, WID_NGRFI_PARENT), SetDataTip(STR_NEWGRF_INSPECT_PARENT_BUTTON, STR_NEWGRF_INSPECT_PARENT_TOOLTIP), + NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NGRFI_SPRITE_DUMP_UNOPT_SEL), + NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_NGRFI_SPRITE_DUMP_UNOPT), SetDataTip(STR_NEWGRF_INSPECT_SPRITE_DUMP_UNOPT, STR_NEWGRF_INSPECT_SPRITE_DUMP_UNOPT_TOOLTIP), + EndContainer(), NWidget(NWID_SELECTION, INVALID_COLOUR, WID_NGRFI_SPRITE_DUMP_SEL), NWidget(WWT_TEXTBTN, COLOUR_GREY, WID_NGRFI_SPRITE_DUMP), SetDataTip(STR_NEWGRF_INSPECT_SPRITE_DUMP, STR_NEWGRF_INSPECT_SPRITE_DUMP_TOOLTIP), EndContainer(), diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp index 5757a62154..e759969e4b 100644 --- a/src/newgrf_spritegroup.cpp +++ b/src/newgrf_spritegroup.cpp @@ -26,6 +26,10 @@ INSTANTIATE_POOL_METHODS(SpriteGroup) TemporaryStorageArray _temp_store; +std::map _deterministic_sg_shadows; +std::map _randomized_sg_shadows; +bool _grfs_loaded_with_sg_shadow_enable = false; + /** * ResolverObject (re)entry point. @@ -702,6 +706,8 @@ static char *GetAdjustOperationName(char *str, const char *last, DeterministicSp return str + seprintf(str, last, "\?\?\?(0x%X)", operation); } +bool SpriteGroupDumper::use_shadows = false; + void SpriteGroupDumper::DumpSpriteGroup(const SpriteGroup *sg, int padding, uint flags) { uint32 highlight_tag = 0; @@ -748,8 +754,22 @@ void SpriteGroupDumper::DumpSpriteGroup(const SpriteGroup *sg, int padding, uint } case SGT_DETERMINISTIC: { const DeterministicSpriteGroup *dsg = (const DeterministicSpriteGroup*)sg; - if (padding == 0 && !dsg->calculated_result && dsg->default_group != nullptr) { - this->top_default_group = dsg->default_group; + + const SpriteGroup *default_group = dsg->default_group; + const std::vector *adjusts = &(dsg->adjusts); + const std::vector *ranges = &(dsg->ranges); + + if (SpriteGroupDumper::use_shadows) { + auto iter = _deterministic_sg_shadows.find(dsg); + if (iter != _deterministic_sg_shadows.end()) { + default_group = iter->second.default_group; + adjusts = &(iter->second.adjusts); + ranges = &(iter->second.ranges); + } + } + + if (padding == 0 && !dsg->calculated_result && default_group != nullptr) { + this->top_default_group = default_group; } if (dsg == this->top_default_group && !(padding == 4 && (flags & SGDF_DEFAULT))) { seprintf(this->buffer, lastof(this->buffer), "%*sTOP LEVEL DEFAULT GROUP: Deterministic (%s, %s), [%u]", @@ -769,7 +789,7 @@ void SpriteGroupDumper::DumpSpriteGroup(const SpriteGroup *sg, int padding, uint print(); emit_start(); padding += 2; - for (const auto &adjust : dsg->adjusts) { + for (const auto &adjust : (*adjusts)) { char *p = this->buffer; if (adjust.variable == 0x7D) { /* Temp storage load */ @@ -832,27 +852,37 @@ void SpriteGroupDumper::DumpSpriteGroup(const SpriteGroup *sg, int padding, uint seprintf(this->buffer, lastof(this->buffer), "%*scalculated_result", padding, ""); print(); } else { - for (const auto &range : dsg->ranges) { + for (const auto &range : (*ranges)) { seprintf(this->buffer, lastof(this->buffer), "%*srange: %X -> %X", padding, "", range.low, range.high); print(); this->DumpSpriteGroup(range.group, padding + 2, 0); } - if (dsg->default_group != nullptr) { + if (default_group != nullptr) { seprintf(this->buffer, lastof(this->buffer), "%*sdefault", padding, ""); print(); - this->DumpSpriteGroup(dsg->default_group, padding + 2, SGDF_DEFAULT); + this->DumpSpriteGroup(default_group, padding + 2, SGDF_DEFAULT); } } break; } case SGT_RANDOMIZED: { const RandomizedSpriteGroup *rsg = (const RandomizedSpriteGroup*)sg; + + const std::vector *groups = &(rsg->groups); + + if (SpriteGroupDumper::use_shadows) { + auto iter = _randomized_sg_shadows.find(rsg); + if (iter != _randomized_sg_shadows.end()) { + groups = &(iter->second.groups); + } + } + seprintf(this->buffer, lastof(this->buffer), "%*sRandom (%s, %s, triggers: %X, count: %X, lowest_randbit: %X, groups: %u) [%u]", padding, "", _sg_scope_names[rsg->var_scope], rsg->cmp_mode == RSG_CMP_ANY ? "ANY" : "ALL", rsg->triggers, rsg->count, rsg->lowest_randbit, (uint)rsg->groups.size(), rsg->nfo_line); print(); emit_start(); - for (const auto &group : rsg->groups) { + for (const auto &group : (*groups)) { this->DumpSpriteGroup(group, padding + 2, 0); } break; diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h index 9d56e0e182..1529d9fc03 100644 --- a/src/newgrf_spritegroup.h +++ b/src/newgrf_spritegroup.h @@ -22,6 +22,8 @@ #include "3rdparty/cpp-btree/btree_set.h" +#include + /** * Gets the value of a so-called newgrf "register". * @param i index of the register @@ -360,6 +362,12 @@ enum DeterministicSpriteGroupFlags : uint8 { }; DECLARE_ENUM_AS_BIT_SET(DeterministicSpriteGroupFlags) +struct DeterministicSpriteGroupShadowCopy { + std::vector adjusts; + std::vector ranges; + const SpriteGroup *default_group; +}; + struct DeterministicSpriteGroup : SpriteGroup { DeterministicSpriteGroup() : SpriteGroup(SGT_DETERMINISTIC) {} @@ -387,6 +395,10 @@ enum RandomizedSpriteGroupCompareMode { RSG_CMP_ALL, }; +struct RandomizedSpriteGroupShadowCopy { + std::vector groups; +}; + struct RandomizedSpriteGroup : SpriteGroup { RandomizedSpriteGroup() : SpriteGroup(SGT_RANDOMIZED) {} @@ -406,6 +418,9 @@ protected: const SpriteGroup *Resolve(ResolverObject &object) const override; }; +extern std::map _deterministic_sg_shadows; +extern std::map _randomized_sg_shadows; +extern bool _grfs_loaded_with_sg_shadow_enable; /* This contains a callback result. A failed callback has a value of * CALLBACK_FAILED */ @@ -631,6 +646,8 @@ enum DumpSpriteGroupPrintOp { using DumpSpriteGroupPrinter = std::function; struct SpriteGroupDumper { + static bool use_shadows; + private: char buffer[1024]; DumpSpriteGroupPrinter print_fn; diff --git a/src/widgets/newgrf_debug_widget.h b/src/widgets/newgrf_debug_widget.h index fcf45dfc0d..4a665fdea1 100644 --- a/src/widgets/newgrf_debug_widget.h +++ b/src/widgets/newgrf_debug_widget.h @@ -24,6 +24,8 @@ enum NewGRFInspectWidgets { WID_NGRFI_DUPLICATE, ///< Duplicate window WID_NGRFI_SPRITE_DUMP, ///< Dump current sprite group WID_NGRFI_SPRITE_DUMP_SEL, ///< Selection widget for WID_NGRFI_SPRITE_DUMP + WID_NGRFI_SPRITE_DUMP_UNOPT, ///< Dump unoptimised sprite group + WID_NGRFI_SPRITE_DUMP_UNOPT_SEL, ///< Selection widget for WID_NGRFI_SPRITE_DUMP_UNOPT }; /** Widgets of the #SpriteAlignerWindow class. */