From 1e5d35634d2b560ca2ce31449cefee837856b08f Mon Sep 17 00:00:00 2001 From: rubidium Date: Tue, 18 May 2010 21:30:56 +0000 Subject: [PATCH] (svn r19855) [1.0] -Backport from trunk: - Fix: [NewGRF] Possible divide-by-zero if a NewGRF checked industry var 42 while the production level was 0 (r19749) - Fix: Do not recenter usually centered windows when resizing main window or changing language, if they have been moved/resized before [FS#3675] (r19746) - Fix: The GUI is controlled by _local_company, not _current_company (r19745) - Fix: NewGRFs could access map bits of not yet constructed industries and houses during construction callbacks (r19748, r19743) - Fix: Check for industry availability more thoroughly and cancel object placement when selecting not available industries [FS#3787] (r19701) --- src/ai/api/ai_industrytype.cpp | 5 +++++ src/industry_cmd.cpp | 4 ++++ src/industry_gui.cpp | 6 ++++-- src/newgrf_house.cpp | 23 ++++++++++++++++------- src/newgrf_house.h | 2 +- src/newgrf_industries.cpp | 12 ++++++++---- src/newgrf_industrytiles.cpp | 26 ++++++++++++++++++++------ src/newgrf_spritegroup.h | 1 + src/town_cmd.cpp | 2 +- src/viewport.cpp | 2 +- src/window.cpp | 27 ++++++++++++++------------- src/window_gui.h | 4 +++- 12 files changed, 78 insertions(+), 36 deletions(-) diff --git a/src/ai/api/ai_industrytype.cpp b/src/ai/api/ai_industrytype.cpp index 993a88993d..8973d7688a 100644 --- a/src/ai/api/ai_industrytype.cpp +++ b/src/ai/api/ai_industrytype.cpp @@ -14,6 +14,7 @@ #include "../../command_type.h" #include "../../strings_func.h" #include "../../industry.h" +#include "../../newgrf_industries.h" #include "../../core/random_func.hpp" /* static */ bool AIIndustryType::IsValidIndustryType(IndustryType industry_type) @@ -88,6 +89,8 @@ /* static */ bool AIIndustryType::CanBuildIndustry(IndustryType industry_type) { if (!IsValidIndustryType(industry_type)) return false; + + if (!::CheckIfCallBackAllowsAvailability(industry_type, IACT_USERCREATION)) return false; if (!::GetIndustrySpec(industry_type)->IsRawIndustry()) return true; /* raw_industry_construction == 1 means "Build as other industries" */ @@ -97,7 +100,9 @@ /* static */ bool AIIndustryType::CanProspectIndustry(IndustryType industry_type) { if (!IsValidIndustryType(industry_type)) return false; + if (!::GetIndustrySpec(industry_type)->IsRawIndustry()) return false; + if (!::CheckIfCallBackAllowsAvailability(industry_type, IACT_USERCREATION)) return false; /* raw_industry_construction == 2 means "prospect" */ return _settings_game.construction.raw_industry_construction == 2; diff --git a/src/industry_cmd.cpp b/src/industry_cmd.cpp index ec62c41a93..d7255e5faf 100644 --- a/src/industry_cmd.cpp +++ b/src/industry_cmd.cpp @@ -1736,6 +1736,10 @@ CommandCost CmdBuildIndustry(TileIndex tile, DoCommandFlag flags, uint32 p1, uin return CMD_ERROR; } + if (_game_mode != GM_EDITOR && !CheckIfCallBackAllowsAvailability(it, IACT_USERCREATION)) { + return CMD_ERROR; + } + const Industry *ind = NULL; if (_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && indspec->IsRawIndustry()) { if (flags & DC_EXEC) { diff --git a/src/industry_gui.cpp b/src/industry_gui.cpp index 02d1144900..9a8bcc7b2e 100644 --- a/src/industry_gui.cpp +++ b/src/industry_gui.cpp @@ -406,8 +406,10 @@ public: this->SetDirty(); - if ((_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && indsp != NULL && indsp->IsRawIndustry()) || - this->selected_type == INVALID_INDUSTRYTYPE) { + if (GetCallbackWnd() == this && + ((_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && indsp != NULL && indsp->IsRawIndustry()) || + this->selected_type == INVALID_INDUSTRYTYPE || + !this->enabled[this->selected_index])) { /* Reset the button state if going to prospecting or "build many industries" */ this->RaiseButtons(); ResetObjectToPlace(); diff --git a/src/newgrf_house.cpp b/src/newgrf_house.cpp index 3ea73c4cdf..dd9a168624 100644 --- a/src/newgrf_house.cpp +++ b/src/newgrf_house.cpp @@ -99,20 +99,25 @@ void DecreaseBuildingCount(Town *t, HouseID house_id) static uint32 HouseGetRandomBits(const ResolverObject *object) { - const TileIndex tile = object->u.house.tile; - return (tile == INVALID_TILE || !IsTileType(tile, MP_HOUSE)) ? 0 : GetHouseRandomBits(tile); + /* Note: Towns build houses over houses. So during construction checks 'tile' may be a valid but unrelated house. */ + TileIndex tile = object->u.house.tile; + assert(IsValidTile(tile) && (object->u.house.not_yet_constructed || IsTileType(tile, MP_HOUSE))); + return object->u.house.not_yet_constructed ? 0 : GetHouseRandomBits(tile); } static uint32 HouseGetTriggers(const ResolverObject *object) { - const TileIndex tile = object->u.house.tile; - return (tile == INVALID_TILE || !IsTileType(tile, MP_HOUSE)) ? 0 : GetHouseTriggers(tile); + /* Note: Towns build houses over houses. So during construction checks 'tile' may be a valid but unrelated house. */ + TileIndex tile = object->u.house.tile; + assert(IsValidTile(tile) && (object->u.house.not_yet_constructed || IsTileType(tile, MP_HOUSE))); + return object->u.house.not_yet_constructed ? 0 : GetHouseTriggers(tile); } static void HouseSetTriggers(const ResolverObject *object, int triggers) { - const TileIndex tile = object->u.house.tile; - if (IsTileType(tile, MP_HOUSE)) SetHouseTriggers(tile, triggers); + TileIndex tile = object->u.house.tile; + assert(!object->u.house.not_yet_constructed && IsValidTile(tile) && IsTileType(tile, MP_HOUSE)); + SetHouseTriggers(tile, triggers); } static uint32 GetNumHouses(HouseID house_id, const Town *town) @@ -370,6 +375,7 @@ static void NewHouseResolver(ResolverObject *res, HouseID house_id, TileIndex ti res->u.house.tile = tile; res->u.house.town = town; res->u.house.house_id = house_id; + res->u.house.not_yet_constructed = false; res->callback = CBID_NO_CALLBACK; res->callback_param1 = 0; @@ -383,15 +389,18 @@ static void NewHouseResolver(ResolverObject *res, HouseID house_id, TileIndex ti res->grffile = (hs != NULL ? hs->grffile : NULL); } -uint16 GetHouseCallback(CallbackID callback, uint32 param1, uint32 param2, HouseID house_id, Town *town, TileIndex tile) +uint16 GetHouseCallback(CallbackID callback, uint32 param1, uint32 param2, HouseID house_id, Town *town, TileIndex tile, bool not_yet_constructed) { ResolverObject object; const SpriteGroup *group; + assert(IsValidTile(tile) && (not_yet_constructed || IsTileType(tile, MP_HOUSE))); + NewHouseResolver(&object, house_id, tile, town); object.callback = callback; object.callback_param1 = param1; object.callback_param2 = param2; + object.u.house.not_yet_constructed = not_yet_constructed; group = SpriteGroup::Resolve(HouseSpec::Get(house_id)->spritegroup, &object); if (group == NULL) return CALLBACK_FAILED; diff --git a/src/newgrf_house.h b/src/newgrf_house.h index 6a6f40cb02..461ff177c7 100644 --- a/src/newgrf_house.h +++ b/src/newgrf_house.h @@ -44,7 +44,7 @@ void DrawNewHouseTile(TileInfo *ti, HouseID house_id); void AnimateNewHouseTile(TileIndex tile); void ChangeHouseAnimationFrame(const struct GRFFile *file, TileIndex tile, uint16 callback_result); -uint16 GetHouseCallback(CallbackID callback, uint32 param1, uint32 param2, HouseID house_id, Town *town, TileIndex tile); +uint16 GetHouseCallback(CallbackID callback, uint32 param1, uint32 param2, HouseID house_id, Town *town, TileIndex tile, bool not_yet_constructed = false); bool CanDeleteHouse(TileIndex tile); diff --git a/src/newgrf_industries.cpp b/src/newgrf_industries.cpp index 965a276f09..c1bfdfdb66 100644 --- a/src/newgrf_industries.cpp +++ b/src/newgrf_industries.cpp @@ -201,6 +201,7 @@ uint32 IndustryGetVariable(const ResolverObject *object, byte variable, byte par uint16 callback = indspec->callback_mask; if (HasBit(callback, CBM_IND_PRODUCTION_CARGO_ARRIVAL) || HasBit(callback, CBM_IND_PRODUCTION_256_TICKS)) { if ((indspec->behaviour & INDUSTRYBEH_PROD_MULTI_HNDLING) != 0) { + if (industry->prod_level == 0) return 0; return min(industry->incoming_cargo_waiting[variable - 0x40] / industry->prod_level, (uint16)0xFFFF); } else { return min(industry->incoming_cargo_waiting[variable - 0x40], (uint16)0xFFFF); @@ -342,18 +343,21 @@ static const SpriteGroup *IndustryResolveReal(const ResolverObject *object, cons static uint32 IndustryGetRandomBits(const ResolverObject *object) { - return object->u.industry.ind == NULL ? 0 : object->u.industry.ind->random; + const Industry *ind = object->u.industry.ind; + return ind != NULL ? ind->random: 0; } static uint32 IndustryGetTriggers(const ResolverObject *object) { - return object->u.industry.ind == NULL ? 0 : object->u.industry.ind->random_triggers; + const Industry *ind = object->u.industry.ind; + return ind != NULL ? ind->random_triggers : 0; } static void IndustrySetTriggers(const ResolverObject *object, int triggers) { - if (object->u.industry.ind == NULL) return; - object->u.industry.ind->random_triggers = triggers; + Industry *ind = object->u.industry.ind; + assert(ind != NULL && ind->index != INVALID_INDUSTRY); + ind->random_triggers = triggers; } static void NewIndustryResolver(ResolverObject *res, TileIndex tile, Industry *indus, IndustryType type) diff --git a/src/newgrf_industrytiles.cpp b/src/newgrf_industrytiles.cpp index 70eebe0a83..9f8f79c5bb 100644 --- a/src/newgrf_industrytiles.cpp +++ b/src/newgrf_industrytiles.cpp @@ -118,26 +118,35 @@ static const SpriteGroup *IndustryTileResolveReal(const ResolverObject *object, static uint32 IndustryTileGetRandomBits(const ResolverObject *object) { const TileIndex tile = object->u.industry.tile; - if (tile == INVALID_TILE || !IsTileType(tile, MP_INDUSTRY)) return 0; - return (object->scope == VSG_SCOPE_SELF) ? GetIndustryRandomBits(tile) : Industry::GetByTile(tile)->random; + const Industry *ind = object->u.industry.ind; + assert(ind != NULL && IsValidTile(tile)); + assert(ind->index == INVALID_INDUSTRY || IsTileType(tile, MP_INDUSTRY)); + + return (object->scope == VSG_SCOPE_SELF) ? + (ind->index != INVALID_INDUSTRY ? GetIndustryRandomBits(tile) : 0) : + ind->random; } static uint32 IndustryTileGetTriggers(const ResolverObject *object) { const TileIndex tile = object->u.industry.tile; - if (tile == INVALID_TILE || !IsTileType(tile, MP_INDUSTRY)) return 0; - return (object->scope == VSG_SCOPE_SELF) ? GetIndustryTriggers(tile) : Industry::GetByTile(tile)->random_triggers; + const Industry *ind = object->u.industry.ind; + assert(ind != NULL && IsValidTile(tile)); + assert(ind->index == INVALID_INDUSTRY || IsTileType(tile, MP_INDUSTRY)); + if (ind->index == INVALID_INDUSTRY) return 0; + return (object->scope == VSG_SCOPE_SELF) ? GetIndustryTriggers(tile) : ind->random_triggers; } static void IndustryTileSetTriggers(const ResolverObject *object, int triggers) { const TileIndex tile = object->u.industry.tile; - if (tile == INVALID_TILE || !IsTileType(tile, MP_INDUSTRY)) return; + Industry *ind = object->u.industry.ind; + assert(ind != NULL && ind->index != INVALID_INDUSTRY && IsValidTile(tile) && IsTileType(tile, MP_INDUSTRY)); if (object->scope == VSG_SCOPE_SELF) { SetIndustryTriggers(tile, triggers); } else { - Industry::GetByTile(tile)->random_triggers = triggers; + ind->random_triggers = triggers; } } @@ -194,6 +203,9 @@ uint16 GetIndustryTileCallback(CallbackID callback, uint32 param1, uint32 param2 ResolverObject object; const SpriteGroup *group; + assert(industry != NULL && IsValidTile(tile)); + assert(industry->index == INVALID_INDUSTRY || IsTileType(tile, MP_INDUSTRY)); + NewIndustryTileResolver(&object, gfx_id, tile, industry); object.callback = callback; object.callback_param1 = param1; @@ -385,6 +397,8 @@ static void DoTriggerIndustryTile(TileIndex tile, IndustryTileTrigger trigger, I { ResolverObject object; + assert(IsValidTile(tile) && IsTileType(tile, MP_INDUSTRY)); + IndustryGfx gfx = GetIndustryGfx(tile); const IndustryTileSpec *itspec = GetIndustryTileSpec(gfx); diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h index 2a2ca216e5..84aff8aa7d 100644 --- a/src/newgrf_spritegroup.h +++ b/src/newgrf_spritegroup.h @@ -319,6 +319,7 @@ struct ResolverObject { TileIndex tile; Town *town; HouseID house_id; + bool not_yet_constructed; ///< True for construction check } house; struct { TileIndex tile; diff --git a/src/town_cmd.cpp b/src/town_cmd.cpp index 200e81ebe9..1d72842c10 100644 --- a/src/town_cmd.cpp +++ b/src/town_cmd.cpp @@ -2164,7 +2164,7 @@ static bool BuildTownHouse(Town *t, TileIndex tile) } if (HasBit(hs->callback_mask, CBM_HOUSE_ALLOW_CONSTRUCTION)) { - uint16 callback_res = GetHouseCallback(CBID_HOUSE_ALLOW_CONSTRUCTION, 0, 0, house, t, tile); + uint16 callback_res = GetHouseCallback(CBID_HOUSE_ALLOW_CONSTRUCTION, 0, 0, house, t, tile, true); if (callback_res != CALLBACK_FAILED && GB(callback_res, 0, 8) == 0) continue; } diff --git a/src/viewport.cpp b/src/viewport.cpp index c9b6746056..09bc1efe6a 100644 --- a/src/viewport.cpp +++ b/src/viewport.cpp @@ -1776,7 +1776,7 @@ static bool CheckClickOnStation(const ViewPort *vp, int x, int y) static bool CheckClickOnSign(const ViewPort *vp, int x, int y) { /* Signs are turned off, or they are transparent and invisibility is ON, or company is a spectator */ - if (!HasBit(_display_opt, DO_SHOW_SIGNS) || IsInvisibilitySet(TO_SIGNS) || _current_company == COMPANY_SPECTATOR) return false; + if (!HasBit(_display_opt, DO_SHOW_SIGNS) || IsInvisibilitySet(TO_SIGNS) || _local_company == COMPANY_SPECTATOR) return false; const Sign *si; FOR_ALL_SIGNS(si) { diff --git a/src/window.cpp b/src/window.cpp index 78fbd1ac5c..e668f5b56f 100644 --- a/src/window.cpp +++ b/src/window.cpp @@ -830,22 +830,22 @@ static void BringWindowToFront(Window *w) /** * Initializes the data (except the position and initial size) of a new Window. - * @param cls Class of the window, used for identification and grouping. @see WindowClass + * @param desc Window description. * @param window_number Number being assigned to the new window - * @param desc_flags Window flags. @see WindowDefaultFlag * @return Window pointer of the newly created window * @pre If nested widgets are used (\a widget is \c NULL), #nested_root and #nested_array_size must be initialized. * In addition, #nested_array is either \c NULL, or already initialized. */ -void Window::InitializeData(WindowClass cls, int window_number, uint32 desc_flags) +void Window::InitializeData(const WindowDesc *desc, WindowNumber window_number) { /* Set up window properties; some of them are needed to set up smallest size below */ - this->window_class = cls; + this->window_class = desc->cls; this->flags4 |= WF_WHITE_BORDER_MASK; // just opened windows have a white border + if (desc->default_pos == WDP_CENTER) this->flags4 |= WF_CENTERED; this->owner = INVALID_OWNER; this->nested_focus = NULL; this->window_number = window_number; - this->desc_flags = desc_flags; + this->desc_flags = desc->flags; this->OnInit(); /* Initialize nested widget tree. */ @@ -1230,7 +1230,7 @@ void Window::CreateNestedTree(const WindowDesc *desc, bool fill_nested) */ void Window::FinishInitNested(const WindowDesc *desc, WindowNumber window_number) { - this->InitializeData(desc->cls, window_number, desc->flags); + this->InitializeData(desc, window_number); Point pt = this->OnInitialPosition(desc, this->nested_root->smallest_x, this->nested_root->smallest_y, window_number); this->InitializePositionSize(pt.x, pt.y, this->nested_root->smallest_x, this->nested_root->smallest_y); this->FindWindowPlacementAndResize(desc->default_width, desc->default_height); @@ -1697,6 +1697,7 @@ static bool HandleWindowDragging() static void StartWindowDrag(Window *w) { w->flags4 |= WF_DRAGGING; + w->flags4 &= ~WF_CENTERED; _dragging_window = true; _drag_delta.x = w->left - _cursor.pos.x; @@ -1714,6 +1715,7 @@ static void StartWindowDrag(Window *w) static void StartWindowSizing(Window *w, bool to_left) { w->flags4 |= to_left ? WF_SIZING_LEFT : WF_SIZING_RIGHT; + w->flags4 &= ~WF_CENTERED; _dragging_window = true; _drag_delta.x = _cursor.pos.x; @@ -2556,13 +2558,6 @@ void RelocateAllWindows(int neww, int newh) left = PositionMainToolbar(w); // changes toolbar orientation break; - case WC_SELECT_GAME: - case WC_GAME_OPTIONS: - case WC_NETWORK_WINDOW: - top = (newh - w->height) >> 1; - left = (neww - w->width) >> 1; - break; - case WC_NEWS_WINDOW: top = newh - w->height; left = (neww - w->width) >> 1; @@ -2585,6 +2580,12 @@ void RelocateAllWindows(int neww, int newh) continue; default: { + if (w->flags4 & WF_CENTERED) { + top = (newh - w->height) >> 1; + left = (neww - w->width) >> 1; + break; + } + left = w->left; if (left + (w->width >> 1) >= neww) left = neww - w->width; if (left < 0) left = 0; diff --git a/src/window_gui.h b/src/window_gui.h index 2956b7eabb..5d36b59bd4 100644 --- a/src/window_gui.h +++ b/src/window_gui.h @@ -334,7 +334,7 @@ struct Window : ZeroedMemoryAllocator { }; protected: - void InitializeData(WindowClass cls, int window_number, uint32 desc_flags); + void InitializeData(const WindowDesc *desc, WindowNumber window_number); void InitializePositionSize(int x, int y, int min_width, int min_height); void FindWindowPlacementAndResize(int def_width, int def_height); @@ -842,6 +842,8 @@ enum WindowFlags { WF_WHITE_BORDER_ONE = 1 << 13, WF_WHITE_BORDER_MASK = 1 << 14 | WF_WHITE_BORDER_ONE, + + WF_CENTERED = 1 << 15, ///< Window is centered and shall stay centered after ReInit }; Window *BringWindowToFrontById(WindowClass cls, WindowNumber number);