tron@2186: /* $Id$ */ tron@2186: belugas@6527: /** @file industry_cmd.cpp */ belugas@6527: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" tron@3144: #include "clear_map.h" tron@3314: #include "industry_map.h" tron@3338: #include "station_map.h" rubidium@8387: #include "train.h" maedhros@6669: #include "landscape.h" rubidium@8721: #include "viewport_func.h" rubidium@8612: #include "command_func.h" truelight@0: #include "industry.h" truelight@0: #include "town.h" truelight@0: #include "news.h" truelight@0: #include "saveload.h" tron@2159: #include "variables.h" truelight@4300: #include "genworld.h" belugas@5056: #include "water_map.h" belugas@6160: #include "tree_map.h" peter1138@6417: #include "cargotype.h" glx@8111: #include "newgrf.h" belugas@7789: #include "newgrf_commons.h" rubidium@7664: #include "newgrf_industries.h" belugas@7466: #include "newgrf_industrytiles.h" belugas@7466: #include "newgrf_callbacks.h" rubidium@7886: #include "misc/autoptr.hpp" rubidium@8078: #include "autoslope.h" belugas@8345: #include "transparency.h" rubidium@8444: #include "water.h" rubidium@8610: #include "strings_func.h" rubidium@8615: #include "tile_cmd.h" rubidium@8627: #include "functions.h" rubidium@8627: #include "window_func.h" rubidium@8636: #include "date_func.h" rubidium@8640: #include "vehicle_func.h" rubidium@8653: #include "sound_func.h" truelight@0: rubidium@8760: #include "table/strings.h" rubidium@8760: #include "table/sprites.h" rubidium@8760: #include "table/industry_land.h" rubidium@8760: #include "table/build_industry.h" rubidium@8760: truelight@4403: void ShowIndustryViewWindow(int industry); truelight@4403: void BuildOilRig(TileIndex tile); truelight@4403: truelight@4403: static byte _industry_sound_ctr; truelight@4403: static TileIndex _industry_sound_tile; truelight@4403: belugas@7320: int _total_industries; //general counter belugas@7320: uint16 _industry_counts[NUM_INDUSTRYTYPES]; // Number of industries per type ingame belugas@7320: belugas@7320: const Industry **_industry_sort; belugas@7320: bool _industry_sort_dirty; belugas@7320: belugas@7239: IndustrySpec _industry_specs[NUM_INDUSTRYTYPES]; belugas@7239: IndustryTileSpec _industry_tile_specs[NUM_INDUSTRYTILES]; belugas@7239: belugas@7239: /** This function initialize the spec arrays of both belugas@7239: * industry and industry tiles. belugas@7239: * It adjusts the enabling of the industry too, based on climate availability. belugas@7239: * This will allow for clearer testings */ belugas@7239: void ResetIndustries() belugas@7239: { belugas@7239: memset(&_industry_specs, 0, sizeof(_industry_specs)); belugas@7239: memcpy(&_industry_specs, &_origin_industry_specs, sizeof(_origin_industry_specs)); belugas@7239: belugas@7239: /* once performed, enable only the current climate industries */ belugas@7239: for (IndustryType i = 0; i < NUM_INDUSTRYTYPES; i++) { rubidium@8150: _industry_specs[i].enabled = i < NEW_INDUSTRYOFFSET && skidd13@8424: HasBit(_origin_industry_specs[i].climate_availability, _opt.landscape); belugas@7239: } belugas@7239: belugas@7239: memset(&_industry_tile_specs, 0, sizeof(_industry_tile_specs)); belugas@7239: memcpy(&_industry_tile_specs, &_origin_industry_tile_specs, sizeof(_origin_industry_tile_specs)); belugas@8162: belugas@8162: /* Reset any overrides that have been set. */ belugas@8162: _industile_mngr.ResetOverride(); belugas@8162: _industry_mngr.ResetOverride(); belugas@7239: } belugas@7239: belugas@7757: void ResetIndustryCreationProbility(IndustryType type) belugas@7757: { belugas@7757: assert(type < INVALID_INDUSTRYTYPE); belugas@7757: _industry_specs[type].appear_creation[_opt.landscape] = 0; belugas@7757: } belugas@7757: rubidium@7886: DEFINE_OLD_POOL_GENERIC(Industry, Industry) truelight@1267: belugas@3499: /** belugas@3499: * Retrieve the type for this industry. Although it is accessed by a tile, belugas@3499: * it will return the general type of industry, and not the sprite index belugas@3499: * as would do GetIndustryGfx. belugas@3499: * @param tile that is queried belugas@3499: * @pre IsTileType(tile, MP_INDUSTRY) belugas@3499: * @return general type for this industry, as defined in industry.h belugas@3499: **/ belugas@3499: IndustryType GetIndustryType(TileIndex tile) belugas@3499: { belugas@3499: assert(IsTileType(tile, MP_INDUSTRY)); belugas@3499: belugas@7135: const Industry *ind = GetIndustryByTile(tile); rubidium@7886: return ind->IsValid() ? ind->type : (IndustryType)IT_INVALID; belugas@3499: } truelight@0: belugas@3662: /** belugas@3662: * Accessor for array _industry_specs. belugas@3662: * This will ensure at once : proper access and belugas@3662: * not allowing modifications of it. belugas@3669: * @param thistype of industry (which is the index in _industry_specs) belugas@6989: * @pre thistype < NUM_INDUSTRYTYPES belugas@6527: * @return a pointer to the corresponding industry spec belugas@3662: **/ belugas@3689: const IndustrySpec *GetIndustrySpec(IndustryType thistype) belugas@3662: { belugas@6989: assert(thistype < NUM_INDUSTRYTYPES); belugas@3662: return &_industry_specs[thistype]; belugas@3662: } belugas@3662: belugas@6527: /** belugas@6527: * Accessor for array _industry_tile_specs. belugas@6527: * This will ensure at once : proper access and belugas@6527: * not allowing modifications of it. belugas@7784: * @param gfx of industrytile (which is the index in _industry_tile_specs) belugas@7245: * @pre gfx < INVALID_INDUSTRYTILE belugas@6527: * @return a pointer to the corresponding industrytile spec belugas@6527: **/ belugas@7958: const IndustryTileSpec *GetIndustryTileSpec(IndustryGfx gfx) belugas@6418: { belugas@7245: assert(gfx < INVALID_INDUSTRYTILE); belugas@7958: return &_industry_tile_specs[gfx]; belugas@6418: } belugas@6418: rubidium@7886: Industry::~Industry() truelight@4403: { rubidium@7916: if (CleaningPool()) return; rubidium@7916: rubidium@7886: /* Industry can also be destroyed when not fully initialized. rubidium@7886: * This means that we do not have to clear tiles either. */ rubidium@7886: if (this->width == 0) { rubidium@7886: this->xy = 0; rubidium@7886: return; rubidium@7886: } rubidium@7886: rubidium@7886: BEGIN_TILE_LOOP(tile_cur, this->width, this->height, this->xy); truelight@4403: if (IsTileType(tile_cur, MP_INDUSTRY)) { rubidium@7886: if (GetIndustryIndex(tile_cur) == this->index) { truelight@4403: DoClearSquare(tile_cur); truelight@4403: } truelight@4403: } else if (IsTileType(tile_cur, MP_STATION) && IsOilRig(tile_cur)) { truelight@4403: DeleteOilRig(tile_cur); truelight@4403: } rubidium@7886: END_TILE_LOOP(tile_cur, this->width, this->height, this->xy); truelight@4403: rubidium@7886: if (GetIndustrySpec(this->type)->behaviour & INDUSTRYBEH_PLANT_FIELDS) { truelight@4403: /* Remove the farmland and convert it to regular tiles over time. */ rubidium@7886: BEGIN_TILE_LOOP(tile_cur, 42, 42, this->xy - TileDiffXY(21, 21)) { Darkvater@4815: tile_cur = TILE_MASK(tile_cur); truelight@4403: if (IsTileType(tile_cur, MP_CLEAR) && IsClearGround(tile_cur, CLEAR_FIELDS) && rubidium@7886: GetIndustryIndexOfField(tile_cur) == this->index) { truelight@4403: SetIndustryIndexOfField(tile_cur, INVALID_INDUSTRY); truelight@4403: } rubidium@7886: } END_TILE_LOOP(tile_cur, 42, 42, this->xy - TileDiff(21, 21)) truelight@4403: } truelight@4403: truelight@4403: _industry_sort_dirty = true; rubidium@7886: DecIndustryTypeCount(this->type); belugas@7320: rubidium@7886: DeleteSubsidyWithIndustry(this->index); rubidium@7886: DeleteWindowById(WC_INDUSTRY_VIEW, this->index); truelight@4403: InvalidateWindow(WC_INDUSTRY_DIRECTORY, 0); rubidium@7886: this->xy = 0; truelight@4403: } truelight@4403: belugas@3290: static void IndustryDrawSugarMine(const TileInfo *ti) truelight@0: { belugas@7015: const DrawIndustryAnimationStruct *d; truelight@0: tron@3321: if (!IsIndustryCompleted(ti->tile)) return; truelight@201: rubidium@5686: d = &_draw_industry_spec1[GetIndustryAnimationState(ti->tile)]; truelight@0: peter1138@5919: AddChildSpriteScreen(SPR_IT_SUGAR_MINE_SIEVE + d->image_1, PAL_NONE, d->x, 0); truelight@0: belugas@7015: if (d->image_2 != 0) { belugas@7015: AddChildSpriteScreen(SPR_IT_SUGAR_MINE_CLOUDS + d->image_2 - 1, PAL_NONE, 8, 41); belugas@7015: } truelight@0: belugas@7015: if (d->image_3 != 0) { belugas@7015: AddChildSpriteScreen(SPR_IT_SUGAR_MINE_PILE + d->image_3 - 1, PAL_NONE, belugas@7015: _drawtile_proc1[d->image_3 - 1].x, _drawtile_proc1[d->image_3 - 1].y); tron@2639: } truelight@0: } truelight@0: belugas@3290: static void IndustryDrawToffeeQuarry(const TileInfo *ti) truelight@0: { belugas@7015: uint8 x = 0; truelight@201: tron@3321: if (IsIndustryCompleted(ti->tile)) { belugas@7015: x = _industry_anim_offs_toffee[GetIndustryAnimationState(ti->tile)]; belugas@7015: if (x == 0xFF) truelight@0: x = 0; truelight@0: } truelight@0: peter1138@5919: AddChildSpriteScreen(SPR_IT_TOFFEE_QUARRY_SHOVEL, PAL_NONE, 22 - x, 24 + x); peter1138@5919: AddChildSpriteScreen(SPR_IT_TOFFEE_QUARRY_TOFFEE, PAL_NONE, 6, 14); truelight@0: } truelight@0: belugas@3290: static void IndustryDrawBubbleGenerator( const TileInfo *ti) truelight@0: { tron@3321: if (IsIndustryCompleted(ti->tile)) { belugas@7015: AddChildSpriteScreen(SPR_IT_BUBBLE_GENERATOR_BUBBLE, PAL_NONE, 5, _industry_anim_offs_bubbles[GetIndustryAnimationState(ti->tile)]); tron@2639: } else { peter1138@5919: AddChildSpriteScreen(SPR_IT_BUBBLE_GENERATOR_SPRING, PAL_NONE, 3, 67); truelight@0: } truelight@0: } truelight@0: belugas@3290: static void IndustryDrawToyFactory(const TileInfo *ti) truelight@0: { belugas@7015: const DrawIndustryAnimationStruct *d; truelight@0: belugas@7015: d = &_industry_anim_offs_toys[GetIndustryAnimationState(ti->tile)]; truelight@0: truelight@0: if (d->image_1 != 0xFF) { belugas@7015: AddChildSpriteScreen(SPR_IT_TOY_FACTORY_CLAY, PAL_NONE, d->x, 96 + d->image_1); truelight@0: } truelight@0: truelight@0: if (d->image_2 != 0xFF) { peter1138@5919: AddChildSpriteScreen(SPR_IT_TOY_FACTORY_ROBOT, PAL_NONE, 16 - d->image_2 * 2, 100 + d->image_2); truelight@0: } truelight@0: peter1138@5919: AddChildSpriteScreen(SPR_IT_TOY_FACTORY_STAMP, PAL_NONE, 7, d->image_3); peter1138@5919: AddChildSpriteScreen(SPR_IT_TOY_FACTORY_STAMP_HOLDER, PAL_NONE, 0, 42); truelight@0: } truelight@0: belugas@3290: static void IndustryDrawCoalPlantSparks(const TileInfo *ti) truelight@0: { tron@3321: if (IsIndustryCompleted(ti->tile)) { belugas@7015: uint8 image = GetIndustryAnimationState(ti->tile); tron@3321: truelight@0: if (image != 0 && image < 7) { belugas@3290: AddChildSpriteScreen(image + SPR_IT_POWER_PLANT_TRANSFORMERS, peter1138@5919: PAL_NONE, belugas@7015: _coal_plant_sparks[image - 1].x, belugas@7015: _coal_plant_sparks[image - 1].y truelight@0: ); truelight@0: } truelight@0: } truelight@0: } truelight@0: Darkvater@2436: typedef void IndustryDrawTileProc(const TileInfo *ti); truelight@0: static IndustryDrawTileProc * const _industry_draw_tile_procs[5] = { belugas@3290: IndustryDrawSugarMine, belugas@3290: IndustryDrawToffeeQuarry, belugas@3290: IndustryDrawBubbleGenerator, belugas@3290: IndustryDrawToyFactory, belugas@3290: IndustryDrawCoalPlantSparks, truelight@0: }; truelight@0: truelight@0: static void DrawTile_Industry(TileInfo *ti) truelight@0: { belugas@7466: IndustryGfx gfx = GetIndustryGfx(ti->tile); belugas@7466: Industry *ind = GetIndustryByTile(ti->tile); belugas@7466: const IndustryTileSpec *indts = GetIndustryTileSpec(gfx); belugas@3654: const DrawBuildingsTileStruct *dits; peter1138@5919: SpriteID image; peter1138@5919: SpriteID pal; truelight@0: belugas@7466: /* Retrieve pointer to the draw industry tile struct */ belugas@7466: if (gfx >= NEW_INDUSTRYTILEOFFSET) { belugas@7466: /* Draw the tile using the specialized method of newgrf industrytile. belugas@7466: * DrawNewIndustry will return false if ever the resolver could not belugas@7466: * find any sprite to display. So in this case, we will jump on the belugas@7466: * substitute gfx instead. */ belugas@7466: if (indts->grf_prop.spritegroup != NULL && DrawNewIndustryTile(ti, ind, gfx, indts)) { belugas@7466: return; belugas@7466: } else { belugas@7466: /* No sprite group (or no valid one) found, meaning no graphics associated. belugas@7466: * Use the substitute one instead */ belugas@7957: if (indts->grf_prop.subst_id != INVALID_INDUSTRYTILE) { belugas@7957: gfx = indts->grf_prop.subst_id; belugas@7957: /* And point the industrytile spec accordingly */ belugas@7957: indts = GetIndustryTileSpec(gfx); belugas@7957: } belugas@7466: } belugas@7466: } truelight@0: belugas@7466: dits = &_industry_draw_tile_data[gfx << 2 | (indts->anim_state ? belugas@7787: GetIndustryAnimationState(ti->tile) & INDUSTRY_COMPLETED : rubidium@5687: GetIndustryConstructionStage(ti->tile))]; truelight@0: peter1138@5919: image = dits->ground.sprite; skidd13@8424: if (HasBit(image, PALETTE_MODIFIER_COLOR) && dits->ground.pal == PAL_NONE) { peter1138@5919: pal = GENERAL_SPRITE_COLOR(ind->random_color); peter1138@5919: } else { peter1138@5919: pal = dits->ground.pal; peter1138@5919: } truelight@0: rubidium@7831: /* DrawFoundation() modifes ti->z and ti->tileh */ rubidium@7831: if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, FOUNDATION_LEVELED); truelight@0: rubidium@7531: DrawGroundSprite(image, pal); rubidium@7531: truelight@0: /* Add industry on top of the ground? */ peter1138@5919: image = dits->building.sprite; tron@2639: if (image != 0) { rubidium@7829: AddSortableSpriteToDraw(image, skidd13@8424: (HasBit(image, PALETTE_MODIFIER_COLOR) && dits->building.pal == PAL_NONE) ? GENERAL_SPRITE_COLOR(ind->random_color) : dits->building.pal, tron@2653: ti->x + dits->subtile_x, tron@2653: ti->y + dits->subtile_y, rubidium@8073: dits->width, rubidium@8073: dits->height, truelight@0: dits->dz, rubidium@7831: ti->z, belugas@8345: IsTransparencySet(TO_INDUSTRIES)); truelight@0: belugas@8345: if (IsTransparencySet(TO_INDUSTRIES)) return; truelight@0: } truelight@0: tron@2639: { belugas@3654: int proc = dits->draw_proc - 1; tron@2639: if (proc >= 0) _industry_draw_tile_procs[proc](ti); truelight@0: } truelight@0: } truelight@0: tron@4231: static uint GetSlopeZ_Industry(TileIndex tile, uint x, uint y) tron@2537: { tron@4231: return GetTileMaxZ(tile); truelight@0: } truelight@0: rubidium@7831: static Foundation GetFoundation_Industry(TileIndex tile, Slope tileh) tron@2548: { rubidium@7831: return FlatteningFoundation(tileh); dominik@39: } dominik@39: tron@1977: static void GetAcceptedCargo_Industry(TileIndex tile, AcceptedCargo ac) truelight@0: { rubidium@7695: IndustryGfx gfx = GetIndustryGfx(tile); rubidium@7695: const IndustryTileSpec *itspec = GetIndustryTileSpec(gfx); rubidium@7695: rubidium@7695: /* When we have to use a callback, we put our data in the next two variables */ rubidium@7695: CargoID raw_accepts_cargo[lengthof(itspec->accepts_cargo)]; rubidium@7695: uint8 raw_acceptance[lengthof(itspec->acceptance)]; rubidium@7695: rubidium@7695: /* And then these will always point to a same sized array with the required data */ rubidium@7695: const CargoID *accepts_cargo = itspec->accepts_cargo; rubidium@7695: const uint8 *acceptance = itspec->acceptance; rubidium@7695: skidd13@8424: if (HasBit(itspec->callback_flags, CBM_INDT_ACCEPT_CARGO)) { rubidium@7695: uint16 res = GetIndustryTileCallback(CBID_INDTILE_ACCEPT_CARGO, 0, 0, gfx, GetIndustryByTile(tile), tile); rubidium@7695: if (res != CALLBACK_FAILED) { rubidium@7695: accepts_cargo = raw_accepts_cargo; rubidium@7695: for (uint i = 0; i < lengthof(itspec->accepts_cargo); i++) raw_accepts_cargo[i] = GetCargoTranslation(GB(res, i * 5, 5), itspec->grf_prop.grffile); rubidium@7695: } rubidium@7695: } rubidium@7695: skidd13@8424: if (HasBit(itspec->callback_flags, CBM_INDT_CARGO_ACCEPTANCE)) { rubidium@7695: uint16 res = GetIndustryTileCallback(CBID_INDTILE_CARGO_ACCEPTANCE, 0, 0, gfx, GetIndustryByTile(tile), tile); rubidium@7695: if (res != CALLBACK_FAILED) { rubidium@7695: acceptance = raw_acceptance; rubidium@7695: for (uint i = 0; i < lengthof(itspec->accepts_cargo); i++) raw_acceptance[i] = GB(res, i * 4, 4); rubidium@7695: } rubidium@7695: } truelight@0: belugas@7232: for (byte i = 0; i < lengthof(itspec->accepts_cargo); i++) { rubidium@7695: CargoID a = accepts_cargo[i]; rubidium@8151: /* Only set the value once. */ rubidium@8151: if (a != CT_INVALID && ac[a] == 0) ac[a] = acceptance[i]; belugas@7232: } truelight@0: } truelight@0: tron@1977: static void GetTileDesc_Industry(TileIndex tile, TileDesc *td) truelight@0: { belugas@3662: const Industry *i = GetIndustryByTile(tile); truelight@0: truelight@0: td->owner = i->owner; belugas@4942: td->str = GetIndustrySpec(i->type)->name; tron@3321: if (!IsIndustryCompleted(tile)) { tron@534: SetDParamX(td->dparam, 0, td->str); truelight@0: td->str = STR_2058_UNDER_CONSTRUCTION; truelight@0: } truelight@0: } truelight@0: rubidium@7439: static CommandCost ClearTile_Industry(TileIndex tile, byte flags) truelight@0: { belugas@3662: Industry *i = GetIndustryByTile(tile); belugas@6886: const IndustrySpec *indspec = GetIndustrySpec(i->type); truelight@0: rubidium@4434: /* water can destroy industries rubidium@4434: * in editor you can bulldoze industries rubidium@4434: * with magic_bulldozer cheat you can destroy industries rubidium@4434: * (area around OILRIG is water, so water shouldn't flood it rubidium@4434: */ darkvater@149: if ((_current_player != OWNER_WATER && _game_mode != GM_EDITOR && darkvater@149: !_cheats.magic_bulldozer.value) || rubidium@8133: ((flags & DC_AUTO) != 0) || belugas@6886: (_current_player == OWNER_WATER && (indspec->behaviour & INDUSTRYBEH_BUILT_ONWATER))) { belugas@6886: SetDParam(0, indspec->name); truelight@0: return_cmd_error(STR_4800_IN_THE_WAY); truelight@0: } truelight@0: rubidium@7886: if (flags & DC_EXEC) delete i; rubidium@8726: return CommandCost(EXPENSES_CONSTRUCTION, indspec->GetRemovalCost()); truelight@0: } truelight@0: tron@1977: static void TransportIndustryGoods(TileIndex tile) truelight@0: { belugas@3662: Industry *i = GetIndustryByTile(tile); belugas@3669: const IndustrySpec *indspec = GetIndustrySpec(i->type); rubidium@8295: bool moved_cargo = false; truelight@0: rubidium@8295: for (uint j = 0; j < lengthof(i->produced_cargo_waiting); j++) { rubidium@8295: uint cw = min(i->produced_cargo_waiting[j], 255); rubidium@8295: if (cw > indspec->minimal_cargo && i->produced_cargo[j] != CT_INVALID) { rubidium@8295: i->produced_cargo_waiting[j] -= cw; tron@3331: rubidium@8295: /* fluctuating economy? */ rubidium@8295: if (_economy.fluct <= 0) cw = (cw + 1) / 2; rubidium@8295: rubidium@8295: i->this_month_production[j] += cw; rubidium@8295: rubidium@8295: uint am = MoveGoodsToStation(i->xy, i->width, i->height, i->produced_cargo[j], cw); rubidium@8295: i->this_month_transported[j] += am; rubidium@8295: rubidium@8295: moved_cargo |= (am != 0); truelight@0: } truelight@0: } truelight@0: rubidium@8295: if (moved_cargo && !StartStopIndustryTileAnimation(i, IAT_INDUSTRY_DISTRIBUTES_CARGO)) { rubidium@8295: uint newgfx = GetIndustryTileSpec(GetIndustryGfx(tile))->anim_production; truelight@201: rubidium@8295: if (newgfx != INDUSTRYTILE_NOANIM) { rubidium@8295: ResetIndustryConstructionStage(tile); rubidium@8295: SetIndustryCompleted(tile, true); rubidium@8295: SetIndustryGfx(tile, newgfx); rubidium@8295: MarkTileDirtyByTile(tile); rubidium@8295: } truelight@0: } truelight@0: } truelight@0: truelight@0: tron@1977: static void AnimateTile_Industry(TileIndex tile) truelight@0: { tron@3331: byte m; rubidium@7725: IndustryGfx gfx = GetIndustryGfx(tile); truelight@0: rubidium@7725: if (GetIndustryTileSpec(gfx)->animation_info != 0xFFFF) { rubidium@7725: AnimateNewIndustryTile(tile); rubidium@7725: return; rubidium@7725: } rubidium@7725: rubidium@7725: switch (gfx) { belugas@3545: case GFX_SUGAR_MINE_SIEVE: truelight@0: if ((_tick_counter & 1) == 0) { rubidium@5686: m = GetIndustryAnimationState(tile) + 1; truelight@201: tron@2952: switch (m & 7) { rubidium@4434: case 2: SndPlayTileFx(SND_2D_RIP_2, tile); break; tron@541: case 6: SndPlayTileFx(SND_29_RIP, tile); break; truelight@0: } truelight@0: truelight@0: if (m >= 96) { truelight@0: m = 0; truelight@0: DeleteAnimatedTile(tile); truelight@0: } rubidium@5686: SetIndustryAnimationState(tile, m); truelight@0: truelight@0: MarkTileDirtyByTile(tile); truelight@0: } truelight@0: break; truelight@0: belugas@3545: case GFX_TOFFEE_QUARY: truelight@0: if ((_tick_counter & 3) == 0) { rubidium@5686: m = GetIndustryAnimationState(tile); truelight@201: belugas@7015: if (_industry_anim_offs_toffee[m] == 0xFF) { tron@541: SndPlayTileFx(SND_30_CARTOON_SOUND, tile); truelight@0: } truelight@0: truelight@0: if (++m >= 70) { truelight@0: m = 0; truelight@0: DeleteAnimatedTile(tile); truelight@0: } rubidium@5686: SetIndustryAnimationState(tile, m); truelight@0: truelight@0: MarkTileDirtyByTile(tile); truelight@0: } truelight@0: break; truelight@0: belugas@3545: case GFX_BUBBLE_CATCHER: rubidium@5686: if ((_tick_counter & 1) == 0) { rubidium@5686: m = GetIndustryAnimationState(tile); truelight@0: truelight@0: if (++m >= 40) { truelight@0: m = 0; truelight@0: DeleteAnimatedTile(tile); truelight@0: } rubidium@5686: SetIndustryAnimationState(tile, m); truelight@0: truelight@0: MarkTileDirtyByTile(tile); truelight@0: } truelight@0: break; truelight@0: belugas@6527: /* Sparks on a coal plant */ belugas@3545: case GFX_POWERPLANT_SPARKS: truelight@0: if ((_tick_counter & 3) == 0) { rubidium@5686: m = GetIndustryAnimationState(tile); rubidium@5686: if (m == 6) { rubidium@5686: SetIndustryAnimationState(tile, 0); truelight@0: DeleteAnimatedTile(tile); truelight@0: } else { rubidium@5686: SetIndustryAnimationState(tile, m + 1); truelight@0: MarkTileDirtyByTile(tile); truelight@0: } truelight@0: } truelight@0: break; truelight@0: belugas@3545: case GFX_TOY_FACTORY: truelight@0: if ((_tick_counter & 1) == 0) { rubidium@5686: m = GetIndustryAnimationState(tile) + 1; truelight@0: belugas@6964: switch (m) { belugas@6964: case 1: SndPlayTileFx(SND_2C_MACHINERY, tile); break; belugas@6964: case 23: SndPlayTileFx(SND_2B_COMEDY_HIT, tile); break; belugas@6964: case 28: SndPlayTileFx(SND_2A_EXTRACT_AND_POP, tile); break; belugas@6964: default: belugas@6964: if (m >= 50) { belugas@6964: int n = GetIndustryAnimationLoop(tile) + 1; belugas@6964: m = 0; belugas@6964: if (n >= 8) { belugas@6964: n = 0; belugas@6964: DeleteAnimatedTile(tile); belugas@6964: } belugas@6964: SetIndustryAnimationLoop(tile, n); belugas@6964: } truelight@0: } truelight@0: rubidium@5686: SetIndustryAnimationState(tile, m); truelight@0: MarkTileDirtyByTile(tile); truelight@0: } truelight@0: break; truelight@0: rubidium@5685: case GFX_PLASTIC_FOUNTAIN_ANIMATED_1: case GFX_PLASTIC_FOUNTAIN_ANIMATED_2: rubidium@5685: case GFX_PLASTIC_FOUNTAIN_ANIMATED_3: case GFX_PLASTIC_FOUNTAIN_ANIMATED_4: rubidium@5685: case GFX_PLASTIC_FOUNTAIN_ANIMATED_5: case GFX_PLASTIC_FOUNTAIN_ANIMATED_6: rubidium@5685: case GFX_PLASTIC_FOUNTAIN_ANIMATED_7: case GFX_PLASTIC_FOUNTAIN_ANIMATED_8: truelight@0: if ((_tick_counter & 3) == 0) { belugas@3499: IndustryGfx gfx = GetIndustryGfx(tile); truelight@0: tron@3331: gfx = (gfx < 155) ? gfx + 1 : 148; tron@3331: SetIndustryGfx(tile, gfx); truelight@0: MarkTileDirtyByTile(tile); truelight@0: } truelight@0: break; truelight@0: rubidium@4583: case GFX_OILWELL_ANIMATED_1: rubidium@4583: case GFX_OILWELL_ANIMATED_2: rubidium@4583: case GFX_OILWELL_ANIMATED_3: truelight@0: if ((_tick_counter & 7) == 0) { skidd13@8463: bool b = Chance16(1, 7); belugas@3499: IndustryGfx gfx = GetIndustryGfx(tile); tron@3331: rubidium@5686: m = GetIndustryAnimationState(tile) + 1; rubidium@4583: if (m == 4 && (m = 0, ++gfx) == GFX_OILWELL_ANIMATED_3 + 1 && (gfx = GFX_OILWELL_ANIMATED_1, b)) { rubidium@4583: SetIndustryGfx(tile, GFX_OILWELL_NOT_ANIMATED); rubidium@5686: SetIndustryConstructionStage(tile, 3); truelight@201: DeleteAnimatedTile(tile); truelight@0: } else { rubidium@5686: SetIndustryAnimationState(tile, m); tron@3331: SetIndustryGfx(tile, gfx); truelight@0: MarkTileDirtyByTile(tile); truelight@0: } truelight@0: } truelight@0: break; truelight@0: rubidium@4583: case GFX_COAL_MINE_TOWER_ANIMATED: rubidium@4583: case GFX_COPPER_MINE_TOWER_ANIMATED: rubidium@4583: case GFX_GOLD_MINE_TOWER_ANIMATED: { truelight@0: int state = _tick_counter & 0x7FF; truelight@0: truelight@0: if ((state -= 0x400) < 0) truelight@0: return; truelight@0: truelight@0: if (state < 0x1A0) { truelight@0: if (state < 0x20 || state >= 0x180) { rubidium@5686: m = GetIndustryAnimationState(tile); rubidium@5686: if (!(m & 0x40)) { rubidium@5687: SetIndustryAnimationState(tile, m | 0x40); tron@541: SndPlayTileFx(SND_0B_MINING_MACHINERY, tile); truelight@0: } truelight@0: if (state & 7) truelight@0: return; truelight@0: } else { truelight@0: if (state & 3) truelight@0: return; truelight@0: } rubidium@5686: m = (GetIndustryAnimationState(tile) + 1) | 0x40; truelight@0: if (m > 0xC2) m = 0xC0; rubidium@5686: SetIndustryAnimationState(tile, m); truelight@0: MarkTileDirtyByTile(tile); truelight@0: } else if (state >= 0x200 && state < 0x3A0) { truelight@0: int i; truelight@0: i = (state < 0x220 || state >= 0x380) ? 7 : 3; truelight@0: if (state & i) truelight@0: return; truelight@0: rubidium@5686: m = (GetIndustryAnimationState(tile) & 0xBF) - 1; truelight@0: if (m < 0x80) m = 0x82; rubidium@5686: SetIndustryAnimationState(tile, m); truelight@0: MarkTileDirtyByTile(tile); truelight@0: } truelight@0: } break; truelight@0: } truelight@0: } truelight@0: belugas@3290: static void CreateIndustryEffectSmoke(TileIndex tile) truelight@0: { tron@3773: uint x = TileX(tile) * TILE_SIZE; tron@3773: uint y = TileY(tile) * TILE_SIZE; tron@3773: uint z = GetTileMaxZ(tile); tron@3094: tron@3773: CreateEffectVehicle(x + 15, y + 14, z + 59, EV_CHIMNEY_SMOKE); truelight@0: } truelight@0: belugas@3495: static void MakeIndustryTileBigger(TileIndex tile) truelight@0: { belugas@3495: byte cnt = GetIndustryConstructionCounter(tile) + 1; belugas@3495: byte stage; truelight@0: belugas@3495: if (cnt != 4) { belugas@3495: SetIndustryConstructionCounter(tile, cnt); truelight@0: return; truelight@0: } truelight@0: belugas@3495: stage = GetIndustryConstructionStage(tile) + 1; belugas@3495: SetIndustryConstructionCounter(tile, 0); belugas@3495: SetIndustryConstructionStage(tile, stage); rubidium@7725: StartStopIndustryTileAnimation(tile, IAT_CONSTRUCTION_STATE_CHANGE); belugas@7787: if (stage == INDUSTRY_COMPLETED) SetIndustryCompleted(tile, true); truelight@201: truelight@0: MarkTileDirtyByTile(tile); truelight@0: tron@3321: if (!IsIndustryCompleted(tile)) return; truelight@0: rubidium@7725: IndustryGfx gfx = GetIndustryGfx(tile); rubidium@7725: if (gfx >= NEW_INDUSTRYTILEOFFSET) { rubidium@8349: /* New industries are already animated on construction. */ rubidium@7725: return; rubidium@7725: } rubidium@7725: rubidium@7725: switch (gfx) { rubidium@4583: case GFX_POWERPLANT_CHIMNEY: belugas@3290: CreateIndustryEffectSmoke(tile); truelight@0: break; truelight@0: peter1138@4607: case GFX_OILRIG_1: peter1138@4607: if (GetIndustryGfx(tile + TileDiffXY(0, 1)) == GFX_OILRIG_1) BuildOilRig(tile); truelight@0: break; truelight@0: belugas@3545: case GFX_TOY_FACTORY: belugas@3545: case GFX_BUBBLE_CATCHER: belugas@3545: case GFX_TOFFEE_QUARY: rubidium@5686: SetIndustryAnimationState(tile, 0); belugas@3538: SetIndustryAnimationLoop(tile, 0); truelight@0: break; truelight@0: rubidium@5685: case GFX_PLASTIC_FOUNTAIN_ANIMATED_1: case GFX_PLASTIC_FOUNTAIN_ANIMATED_2: rubidium@5685: case GFX_PLASTIC_FOUNTAIN_ANIMATED_3: case GFX_PLASTIC_FOUNTAIN_ANIMATED_4: rubidium@5685: case GFX_PLASTIC_FOUNTAIN_ANIMATED_5: case GFX_PLASTIC_FOUNTAIN_ANIMATED_6: rubidium@5685: case GFX_PLASTIC_FOUNTAIN_ANIMATED_7: case GFX_PLASTIC_FOUNTAIN_ANIMATED_8: truelight@0: AddAnimatedTile(tile); truelight@0: break; truelight@0: } truelight@0: } truelight@0: belugas@3290: static void TileLoopIndustry_BubbleGenerator(TileIndex tile) truelight@0: { truelight@0: int dir; truelight@0: Vehicle *v; truelight@0: static const int8 _tileloop_ind_case_161[12] = { rubidium@4344: 11, 0, -4, -14, rubidium@4344: -4, -10, -4, 1, rubidium@4344: 49, 59, 60, 65, truelight@0: }; truelight@0: tron@541: SndPlayTileFx(SND_2E_EXTRACT_AND_POP, tile); truelight@201: truelight@0: dir = Random() & 3; truelight@0: truelight@0: v = CreateEffectVehicleAbove( celestar@3421: TileX(tile) * TILE_SIZE + _tileloop_ind_case_161[dir + 0], celestar@3421: TileY(tile) * TILE_SIZE + _tileloop_ind_case_161[dir + 4], truelight@0: _tileloop_ind_case_161[dir + 8], tron@1359: EV_BUBBLE truelight@0: ); truelight@0: rubidium@7830: if (v != NULL) v->u.special.animation_substate = dir; truelight@0: } truelight@0: tron@1977: static void TileLoop_Industry(TileIndex tile) truelight@0: { belugas@3499: IndustryGfx newgfx; rubidium@5686: IndustryGfx gfx; truelight@0: rubidium@8356: TriggerIndustryTile(tile, INDTILE_TRIGGER_TILE_LOOP); rubidium@8356: tron@3321: if (!IsIndustryCompleted(tile)) { belugas@3495: MakeIndustryTileBigger(tile); truelight@0: return; truelight@0: } truelight@0: tron@2639: if (_game_mode == GM_EDITOR) return; truelight@0: truelight@0: TransportIndustryGoods(tile); truelight@0: rubidium@7725: if (StartStopIndustryTileAnimation(tile, IAT_TILELOOP)) return; rubidium@7725: belugas@6631: newgfx = GetIndustryTileSpec(GetIndustryGfx(tile))->anim_next; belugas@7239: if (newgfx != INDUSTRYTILE_NOANIM) { belugas@3495: ResetIndustryConstructionStage(tile); tron@3331: SetIndustryGfx(tile, newgfx); truelight@0: MarkTileDirtyByTile(tile); truelight@0: return; truelight@0: } truelight@0: rubidium@5686: gfx = GetIndustryGfx(tile); truelight@0: rubidium@5686: switch (gfx) { rubidium@4583: case GFX_OILRIG_1: // coast line at oilrigs rubidium@4583: case GFX_OILRIG_2: rubidium@4583: case GFX_OILRIG_3: rubidium@4583: case GFX_OILRIG_4: rubidium@4583: case GFX_OILRIG_5: dominik@43: TileLoop_Water(tile); dominik@43: break; dominik@43: rubidium@4583: case GFX_COAL_MINE_TOWER_NOT_ANIMATED: rubidium@4583: case GFX_COPPER_MINE_TOWER_NOT_ANIMATED: rubidium@4583: case GFX_GOLD_MINE_TOWER_NOT_ANIMATED: skidd13@8463: if (!(_tick_counter & 0x400) && Chance16(1, 2)) { rubidium@5686: switch (gfx) { rubidium@5686: case GFX_COAL_MINE_TOWER_NOT_ANIMATED: gfx = GFX_COAL_MINE_TOWER_ANIMATED; break; rubidium@5686: case GFX_COPPER_MINE_TOWER_NOT_ANIMATED: gfx = GFX_COPPER_MINE_TOWER_ANIMATED; break; rubidium@5686: case GFX_GOLD_MINE_TOWER_NOT_ANIMATED: gfx = GFX_GOLD_MINE_TOWER_ANIMATED; break; rubidium@5686: } rubidium@5686: SetIndustryGfx(tile, gfx); rubidium@5686: SetIndustryAnimationState(tile, 0x80); rubidium@5686: AddAnimatedTile(tile); rubidium@5686: } truelight@0: break; truelight@0: rubidium@4583: case GFX_OILWELL_NOT_ANIMATED: skidd13@8463: if (Chance16(1, 6)) { rubidium@5686: SetIndustryGfx(tile, GFX_OILWELL_ANIMATED_1); rubidium@5686: SetIndustryAnimationState(tile, 0); rubidium@5686: AddAnimatedTile(tile); rubidium@5686: } truelight@0: break; truelight@0: rubidium@4583: case GFX_COAL_MINE_TOWER_ANIMATED: rubidium@4583: case GFX_COPPER_MINE_TOWER_ANIMATED: rubidium@4583: case GFX_GOLD_MINE_TOWER_ANIMATED: rubidium@5686: if (!(_tick_counter & 0x400)) { rubidium@5686: switch (gfx) { rubidium@5686: case GFX_COAL_MINE_TOWER_ANIMATED: gfx = GFX_COAL_MINE_TOWER_NOT_ANIMATED; break; rubidium@5686: case GFX_COPPER_MINE_TOWER_ANIMATED: gfx = GFX_COPPER_MINE_TOWER_NOT_ANIMATED; break; rubidium@5686: case GFX_GOLD_MINE_TOWER_ANIMATED: gfx = GFX_GOLD_MINE_TOWER_NOT_ANIMATED; break; rubidium@5686: } rubidium@5686: SetIndustryGfx(tile, gfx); rubidium@5686: SetIndustryCompleted(tile, true); rubidium@5686: SetIndustryConstructionStage(tile, 3); rubidium@5686: DeleteAnimatedTile(tile); rubidium@5686: } rubidium@4583: break; rubidium@4583: rubidium@4583: case GFX_POWERPLANT_SPARKS: skidd13@8463: if (Chance16(1, 3)) { tron@541: SndPlayTileFx(SND_0C_ELECTRIC_SPARK, tile); truelight@0: AddAnimatedTile(tile); truelight@0: } truelight@0: break; truelight@0: rubidium@4583: case GFX_COPPER_MINE_CHIMNEY: celestar@3421: CreateEffectVehicleAbove(TileX(tile) * TILE_SIZE + 6, TileY(tile) * TILE_SIZE + 6, 43, EV_SMOKE); tron@2639: break; truelight@0: truelight@0: rubidium@4583: case GFX_TOY_FACTORY: { belugas@3662: Industry *i = GetIndustryByTile(tile); truelight@0: if (i->was_cargo_delivered) { truelight@0: i->was_cargo_delivered = false; belugas@3538: SetIndustryAnimationLoop(tile, 0); truelight@0: AddAnimatedTile(tile); truelight@0: } truelight@0: } truelight@0: break; truelight@0: rubidium@4583: case GFX_BUBBLE_GENERATOR: belugas@3290: TileLoopIndustry_BubbleGenerator(tile); truelight@201: break; truelight@201: rubidium@4583: case GFX_TOFFEE_QUARY: truelight@0: AddAnimatedTile(tile); truelight@0: break; truelight@0: rubidium@4583: case GFX_SUGAR_MINE_SIEVE: skidd13@8463: if (Chance16(1, 3)) AddAnimatedTile(tile); truelight@0: break; truelight@0: } truelight@0: } truelight@0: tron@1977: static void ClickTile_Industry(TileIndex tile) truelight@0: { tron@3314: ShowIndustryViewWindow(GetIndustryIndex(tile)); truelight@0: } truelight@0: rubidium@7179: static uint32 GetTileTrackStatus_Industry(TileIndex tile, TransportType mode, uint sub_mode) truelight@0: { truelight@0: return 0; truelight@0: } truelight@0: Darkvater@3344: static void GetProducedCargo_Industry(TileIndex tile, CargoID *b) truelight@0: { glx@8141: const Industry *i = GetIndustryByTile(tile); tron@2630: truelight@0: b[0] = i->produced_cargo[0]; truelight@0: b[1] = i->produced_cargo[1]; truelight@0: } truelight@0: Darkvater@2436: static void ChangeTileOwner_Industry(TileIndex tile, PlayerID old_player, PlayerID new_player) truelight@0: { rubidium@7682: /* If the founder merges, the industry was created by the merged company */ rubidium@7682: Industry *i = GetIndustryByTile(tile); rubidium@7682: if (i->founder == old_player) i->founder = (new_player == PLAYER_SPECTATOR) ? OWNER_NONE : new_player; truelight@0: } truelight@0: truelight@0: static const byte _plantfarmfield_type[] = {1, 1, 1, 1, 1, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6}; truelight@0: tron@1048: static bool IsBadFarmFieldTile(TileIndex tile) truelight@0: { tron@1214: switch (GetTileType(tile)) { belugas@5935: case MP_CLEAR: return IsClearGround(tile, CLEAR_FIELDS) || IsClearGround(tile, CLEAR_SNOW) || IsClearGround(tile, CLEAR_DESERT); frosch@8955: case MP_TREES: return (GetTreeGround(tile) == TREE_GROUND_SHORE); tron@2955: default: return true; truelight@0: } truelight@0: } truelight@0: tron@1048: static bool IsBadFarmFieldTile2(TileIndex tile) truelight@0: { tron@1214: switch (GetTileType(tile)) { belugas@5935: case MP_CLEAR: return IsClearGround(tile, CLEAR_SNOW) || IsClearGround(tile, CLEAR_DESERT); frosch@8955: case MP_TREES: return (GetTreeGround(tile) == TREE_GROUND_SHORE); tron@2955: default: return true; truelight@0: } truelight@0: } truelight@0: tron@3157: static void SetupFarmFieldFence(TileIndex tile, int size, byte type, Axis direction) truelight@0: { truelight@0: do { truelight@0: tile = TILE_MASK(tile); truelight@201: tron@1035: if (IsTileType(tile, MP_CLEAR) || IsTileType(tile, MP_TREES)) { rubidium@5838: byte or_ = type; truelight@201: skidd13@8463: if (or_ == 1 && Chance16(1, 7)) or_ = 2; truelight@0: tron@3157: if (direction == AXIS_X) { rubidium@5838: SetFenceSE(tile, or_); tron@3157: } else { rubidium@5838: SetFenceSW(tile, or_); truelight@0: } truelight@0: } truelight@201: tron@3157: tile += (direction == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); truelight@0: } while (--size); truelight@0: } truelight@0: rubidium@4330: static void PlantFarmField(TileIndex tile, IndustryID industry) truelight@0: { truelight@0: uint size_x, size_y; truelight@0: uint32 r; tron@2133: uint count; tron@2979: uint counter; tron@2979: uint field_type; tron@2979: int type; truelight@0: belugas@6683: if (_opt.landscape == LT_ARCTIC) { maedhros@6669: if (GetTileZ(tile) + TILE_HEIGHT * 2 >= GetSnowLine()) truelight@0: return; truelight@0: } truelight@0: truelight@0: /* determine field size */ truelight@0: r = (Random() & 0x303) + 0x404; belugas@6683: if (_opt.landscape == LT_ARCTIC) r += 0x404; tron@2150: size_x = GB(r, 0, 8); tron@2150: size_y = GB(r, 8, 8); truelight@0: truelight@0: /* offset tile to match size */ tron@1981: tile -= TileDiffXY(size_x / 2, size_y / 2); truelight@201: truelight@0: /* check the amount of bad tiles */ truelight@0: count = 0; truelight@0: BEGIN_TILE_LOOP(cur_tile, size_x, size_y, tile) truelight@0: cur_tile = TILE_MASK(cur_tile); truelight@0: count += IsBadFarmFieldTile(cur_tile); truelight@0: END_TILE_LOOP(cur_tile, size_x, size_y, tile) tron@2133: if (count * 2 >= size_x * size_y) return; truelight@0: truelight@0: /* determine type of field */ truelight@0: r = Random(); tron@2979: counter = GB(r, 5, 3); tron@2979: field_type = GB(r, 8, 8) * 9 >> 8; truelight@0: truelight@0: /* make field */ truelight@0: BEGIN_TILE_LOOP(cur_tile, size_x, size_y, tile) truelight@0: cur_tile = TILE_MASK(cur_tile); truelight@0: if (!IsBadFarmFieldTile2(cur_tile)) { truelight@4328: MakeField(cur_tile, field_type, industry); tron@2979: SetClearCounter(cur_tile, counter); tron@2979: MarkTileDirtyByTile(cur_tile); truelight@0: } truelight@0: END_TILE_LOOP(cur_tile, size_x, size_y, tile) truelight@201: truelight@0: type = 3; belugas@6683: if (_opt.landscape != LT_ARCTIC && _opt.landscape != LT_TROPIC) { truelight@0: type = _plantfarmfield_type[Random() & 0xF]; truelight@0: } truelight@0: tron@3157: SetupFarmFieldFence(tile - TileDiffXY(1, 0), size_y, type, AXIS_Y); tron@3157: SetupFarmFieldFence(tile - TileDiffXY(0, 1), size_x, type, AXIS_X); tron@3157: SetupFarmFieldFence(tile + TileDiffXY(size_x - 1, 0), size_y, type, AXIS_Y); tron@3157: SetupFarmFieldFence(tile + TileDiffXY(0, size_y - 1), size_x, type, AXIS_X); truelight@0: } truelight@0: truelight@4328: void PlantRandomFarmField(const Industry *i) truelight@4328: { truelight@4328: int x = i->width / 2 + Random() % 31 - 16; truelight@4328: int y = i->height / 2 + Random() % 31 - 16; truelight@4328: truelight@4328: TileIndex tile = TileAddWrap(i->xy, x, y); truelight@4328: truelight@4328: if (tile != INVALID_TILE) PlantFarmField(tile, i->index); truelight@4328: } truelight@4328: belugas@5118: /** belugas@5118: * Search callback function for ChopLumberMillTrees belugas@5118: * @param tile to test belugas@5118: * @param data that is passed by the caller. In this case, nothing belugas@6527: * @return the result of the test belugas@5118: */ belugas@5118: static bool SearchLumberMillTrees(TileIndex tile, uint32 data) belugas@5118: { belugas@6160: if (IsTileType(tile, MP_TREES) && GetTreeGrowth(tile) > 2) { ///< 3 and up means all fully grown trees belugas@5118: PlayerID old_player = _current_player; belugas@5118: /* found a tree */ belugas@5118: belugas@5118: _current_player = OWNER_NONE; belugas@5118: _industry_sound_ctr = 1; belugas@5118: _industry_sound_tile = tile; belugas@5118: SndPlayTileFx(SND_38_CHAINSAW, tile); belugas@5118: belugas@5118: DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR); belugas@5118: belugas@5118: _current_player = old_player; belugas@5118: return true; belugas@5118: } belugas@5118: return false; belugas@5118: } belugas@5118: belugas@5118: /** belugas@5118: * Perform a circular search around the Lumber Mill in order to find trees to cut belugas@5118: * @param i industry belugas@5118: */ truelight@0: static void ChopLumberMillTrees(Industry *i) truelight@0: { tron@1977: TileIndex tile = i->xy; tron@2639: belugas@5118: if (!IsIndustryCompleted(tile)) return; ///< Can't proceed if not completed truelight@0: belugas@5118: if (CircularTileSearch(tile, 40, SearchLumberMillTrees, 0)) ///< 40x40 tiles to search rubidium@7661: i->produced_cargo_waiting[0] = min(0xffff, i->produced_cargo_waiting[0] + 45); ///< Found a tree, add according value to waiting cargo truelight@0: } truelight@0: truelight@0: static void ProduceIndustryGoods(Industry *i) truelight@0: { truelight@0: uint32 r; truelight@0: uint num; belugas@7151: const IndustrySpec *indsp = GetIndustrySpec(i->type); truelight@0: truelight@0: /* play a sound? */ truelight@0: if ((i->counter & 0x3F) == 0) { skidd13@8463: if (Chance16R(1, 14, r) && (num = indsp->number_of_sounds) != 0) { truelight@0: SndPlayTileFx( belugas@7151: (SoundFx)(indsp->random_sounds[((r >> 16) * num) >> 16]), truelight@0: i->xy); truelight@0: } truelight@0: } truelight@0: truelight@0: i->counter--; truelight@0: truelight@0: /* produce some cargo */ truelight@0: if ((i->counter & 0xFF) == 0) { skidd13@8424: if (HasBit(indsp->callback_flags, CBM_IND_PRODUCTION_256_TICKS)) IndustryProductionCallback(i, 1); rubidium@7664: glx@8253: IndustryBehaviour indbehav = indsp->behaviour; rubidium@7661: i->produced_cargo_waiting[0] = min(0xffff, i->produced_cargo_waiting[0] + i->production_rate[0]); rubidium@7661: i->produced_cargo_waiting[1] = min(0xffff, i->produced_cargo_waiting[1] + i->production_rate[1]); truelight@0: rubidium@7701: if ((indbehav & INDUSTRYBEH_PLANT_FIELDS) != 0) { rubidium@7701: bool plant; skidd13@8424: if (HasBit(indsp->callback_flags, CBM_IND_SPECIAL_EFFECT)) { rubidium@7701: plant = (GetIndustryCallback(CBID_INDUSTRY_SPECIAL_EFFECT, Random(), 0, i, i->type, i->xy) != 0); rubidium@7701: } else { skidd13@8463: plant = Chance16(1, 8); rubidium@7701: } rubidium@7701: rubidium@7701: if (plant) PlantRandomFarmField(i); rubidium@7701: } rubidium@7701: if ((indbehav & INDUSTRYBEH_CUT_TREES) != 0) { rubidium@7701: bool cut = ((i->counter & 0x1FF) == 0); skidd13@8424: if (HasBit(indsp->callback_flags, CBM_IND_SPECIAL_EFFECT)) { rubidium@7701: cut = (GetIndustryCallback(CBID_INDUSTRY_SPECIAL_EFFECT, 0, 1, i, i->type, i->xy) != 0); rubidium@7701: } rubidium@7701: rubidium@7701: if (cut) ChopLumberMillTrees(i); tron@2639: } truelight@0: } truelight@0: } truelight@0: rubidium@6573: void OnTick_Industry() truelight@0: { truelight@0: Industry *i; truelight@0: truelight@0: if (_industry_sound_ctr != 0) { truelight@0: _industry_sound_ctr++; truelight@0: truelight@0: if (_industry_sound_ctr == 75) { tron@541: SndPlayTileFx(SND_37_BALLOON_SQUEAK, _industry_sound_tile); truelight@0: } else if (_industry_sound_ctr == 160) { truelight@201: _industry_sound_ctr = 0; tron@541: SndPlayTileFx(SND_36_CARTOON_CRASH, _industry_sound_tile); truelight@0: } truelight@0: } truelight@0: tron@2639: if (_game_mode == GM_EDITOR) return; truelight@0: truelight@830: FOR_ALL_INDUSTRIES(i) { rubidium@8356: TriggerIndustry(i, INDUSTRY_TRIGGER_INDUSTRY_TICK); rubidium@7725: StartStopIndustryTileAnimation(i, IAT_INDUSTRY_TICK); truelight@4346: ProduceIndustryGoods(i); truelight@0: } truelight@0: } truelight@0: tron@3877: static bool CheckNewIndustry_NULL(TileIndex tile) truelight@0: { truelight@0: return true; truelight@0: } truelight@0: tron@3877: static bool CheckNewIndustry_Forest(TileIndex tile) truelight@0: { belugas@6683: if (_opt.landscape == LT_ARCTIC) { maedhros@6669: if (GetTileZ(tile) < HighestSnowLine() + TILE_HEIGHT * 2U) { truelight@0: _error_message = STR_4831_FOREST_CAN_ONLY_BE_PLANTED; truelight@0: return false; truelight@0: } truelight@0: } truelight@0: return true; truelight@0: } truelight@0: tron@3877: static bool CheckNewIndustry_OilRefinery(TileIndex tile) tron@3877: { tron@3877: if (_game_mode == GM_EDITOR) return true; truelight@4300: if (DistanceFromEdge(TILE_ADDXY(tile, 1, 1)) < _patches.oil_refinery_limit) return true; tron@3877: tron@3877: _error_message = STR_483B_CAN_ONLY_BE_POSITIONED; tron@3877: return false; tron@3877: } tron@3877: dominik@7: extern bool _ignore_restrictions; dominik@7: tron@3877: static bool CheckNewIndustry_OilRig(TileIndex tile) truelight@0: { tron@2639: if (_game_mode == GM_EDITOR && _ignore_restrictions) return true; tron@3877: if (TileHeight(tile) == 0 && truelight@4300: DistanceFromEdge(TILE_ADDXY(tile, 1, 1)) < _patches.oil_refinery_limit) return true; truelight@0: truelight@0: _error_message = STR_483B_CAN_ONLY_BE_POSITIONED; truelight@0: return false; truelight@0: } truelight@0: tron@3877: static bool CheckNewIndustry_Farm(TileIndex tile) truelight@0: { belugas@6683: if (_opt.landscape == LT_ARCTIC) { maedhros@6669: if (GetTileZ(tile) + TILE_HEIGHT * 2 >= HighestSnowLine()) { truelight@0: _error_message = STR_0239_SITE_UNSUITABLE; truelight@0: return false; truelight@0: } truelight@0: } truelight@0: return true; truelight@0: } truelight@0: tron@3877: static bool CheckNewIndustry_Plantation(TileIndex tile) truelight@0: { belugas@3379: if (GetTropicZone(tile) == TROPICZONE_DESERT) { truelight@0: _error_message = STR_0239_SITE_UNSUITABLE; truelight@0: return false; truelight@0: } truelight@0: truelight@0: return true; truelight@0: } truelight@0: tron@3877: static bool CheckNewIndustry_Water(TileIndex tile) truelight@0: { belugas@3379: if (GetTropicZone(tile) != TROPICZONE_DESERT) { truelight@0: _error_message = STR_0318_CAN_ONLY_BE_BUILT_IN_DESERT; truelight@0: return false; truelight@0: } truelight@0: truelight@0: return true; truelight@0: } truelight@0: tron@3877: static bool CheckNewIndustry_Lumbermill(TileIndex tile) truelight@0: { belugas@3379: if (GetTropicZone(tile) != TROPICZONE_RAINFOREST) { truelight@0: _error_message = STR_0317_CAN_ONLY_BE_BUILT_IN_RAINFOREST; truelight@0: return false; truelight@0: } truelight@0: return true; truelight@0: } truelight@0: tron@3877: static bool CheckNewIndustry_BubbleGen(TileIndex tile) truelight@0: { tron@3645: return GetTileZ(tile) <= TILE_HEIGHT * 4; truelight@0: } truelight@0: tron@3877: typedef bool CheckNewIndustryProc(TileIndex tile); belugas@3663: static CheckNewIndustryProc * const _check_new_industry_procs[CHECK_END] = { truelight@0: CheckNewIndustry_NULL, truelight@0: CheckNewIndustry_Forest, tron@3877: CheckNewIndustry_OilRefinery, truelight@0: CheckNewIndustry_Farm, truelight@0: CheckNewIndustry_Plantation, truelight@0: CheckNewIndustry_Water, truelight@0: CheckNewIndustry_Lumbermill, truelight@0: CheckNewIndustry_BubbleGen, tron@3877: CheckNewIndustry_OilRig truelight@0: }; truelight@0: tron@1977: static bool CheckSuitableIndustryPos(TileIndex tile) truelight@0: { tron@926: uint x = TileX(tile); tron@926: uint y = TileY(tile); truelight@0: tron@2639: if (x < 2 || y < 2 || x > MapMaxX() - 3 || y > MapMaxY() - 3) { truelight@0: _error_message = STR_0239_SITE_UNSUITABLE; truelight@0: return false; truelight@0: } truelight@0: truelight@0: return true; truelight@0: } truelight@0: belugas@3662: static const Town *CheckMultipleIndustryInTown(TileIndex tile, int type) truelight@0: { belugas@3662: const Town *t; belugas@3662: const Industry *i; truelight@0: truelight@0: t = ClosestTownFromTile(tile, (uint)-1); truelight@201: tron@2639: if (_patches.multiple_industry_per_town) return t; truelight@0: truelight@830: FOR_ALL_INDUSTRIES(i) { truelight@4346: if (i->type == (byte)type && truelight@0: i->town == t) { truelight@0: _error_message = STR_0287_ONLY_ONE_ALLOWED_PER_TOWN; truelight@0: return NULL; truelight@0: } truelight@0: } truelight@0: truelight@0: return t; truelight@0: } truelight@0: rubidium@8158: bool IsSlopeRefused(Slope current, Slope refused) rubidium@8158: { belugas@8229: if (IsSteepSlope(current)) return true; rubidium@8158: if (current != SLOPE_FLAT) { glx@8526: if (IsSteepSlope(refused)) return true; rubidium@8158: rubidium@8158: Slope t = ComplementSlope(current); rubidium@8158: glx@8526: if (refused & SLOPE_W && (t & SLOPE_NW)) return true; glx@8526: if (refused & SLOPE_S && (t & SLOPE_NE)) return true; glx@8526: if (refused & SLOPE_E && (t & SLOPE_SW)) return true; glx@8526: if (refused & SLOPE_N && (t & SLOPE_SE)) return true; rubidium@8158: } rubidium@8158: rubidium@8158: return false; rubidium@8158: } rubidium@8158: rubidium@8158: static bool CheckIfIndustryTilesAreFree(TileIndex tile, const IndustryTileTable *it, uint itspec_index, int type, bool *custom_shape_check = NULL) truelight@0: { truelight@0: _error_message = STR_0239_SITE_UNSUITABLE; rubidium@8159: bool refused_slope = false; rubidium@8159: bool custom_shape = false; truelight@0: truelight@0: do { belugas@7963: IndustryGfx gfx = GetTranslatedIndustryTileID(it->gfx); tron@1977: TileIndex cur_tile = tile + ToTileIndexDiff(it->ti); tron@1977: truelight@0: if (!IsValidTile(cur_tile)) { belugas@7969: if (gfx == GFX_WATERTILE_SPECIALCHECK) continue; truelight@0: return false; truelight@0: } truelight@201: belugas@7969: if (gfx == GFX_WATERTILE_SPECIALCHECK) { tron@3296: if (!IsTileType(cur_tile, MP_WATER) || tron@3636: GetTileSlope(cur_tile, NULL) != SLOPE_FLAT) { tron@3296: return false; tron@3296: } truelight@0: } else { rubidium@8254: if (!EnsureNoVehicleOnGround(cur_tile)) return false; rubidium@7389: if (MayHaveBridgeAbove(cur_tile) && IsBridgeAbove(cur_tile)) return false; rubidium@7389: belugas@8027: const IndustryTileSpec *its = GetIndustryTileSpec(gfx); belugas@8027: glx@8253: IndustryBehaviour ind_behav = GetIndustrySpec(type)->behaviour; truelight@0: glx@8252: /* Perform land/water check if not disabled */ peter1138@8879: if (!HasBit(its->slopes_refused, 5) && ((IsWaterTile(cur_tile) || IsRiverTile(cur_tile)) == !(ind_behav & INDUSTRYBEH_BUILT_ONWATER))) return false; glx@8252: skidd13@8424: if (HasBit(its->callback_flags, CBM_INDT_SHAPE_CHECK)) { rubidium@8159: custom_shape = true; rubidium@8158: if (!PerformIndustryTileSlopeCheck(tile, cur_tile, its, type, gfx, itspec_index)) return false; truelight@0: } else { glx@8252: Slope tileh = GetTileSlope(cur_tile, NULL); glx@8252: refused_slope |= IsSlopeRefused(tileh, its->slopes_refused); rubidium@7708: } truelight@0: glx@8550: if (ind_behav & (INDUSTRYBEH_ONLY_INTOWN | INDUSTRYBEH_TOWN1200_MORE)) { rubidium@7708: if (!IsTileType(cur_tile, MP_HOUSE)) { rubidium@8545: _error_message = STR_030D_CAN_ONLY_BE_BUILT_IN_TOWNS; rubidium@7708: return false; rubidium@7708: } rubidium@7723: if (CmdFailed(DoCommand(cur_tile, 0, 0, 0, CMD_LANDSCAPE_CLEAR))) return false; rubidium@7723: } else if ((ind_behav & INDUSTRYBEH_ONLY_NEARTOWN) == 0 || !IsTileType(cur_tile, MP_HOUSE)) { rubidium@7723: if (CmdFailed(DoCommand(cur_tile, 0, 0, DC_AUTO, CMD_LANDSCAPE_CLEAR))) return false; truelight@0: } truelight@0: } tron@909: } while ((++it)->ti.x != -0x80); truelight@0: rubidium@8159: if (custom_shape_check != NULL) *custom_shape_check = custom_shape; rubidium@8159: rubidium@8159: /* It is almost impossible to have a fully flat land in TG, so what we rubidium@8159: * do is that we check if we can make the land flat later on. See rubidium@8159: * CheckIfCanLevelIndustryPlatform(). */ rubidium@8545: return !refused_slope || (_patches.land_generator == LG_TERRAGENESIS && _generating_world && !custom_shape && !_ignore_restrictions); truelight@0: } truelight@0: truelight@4300: static bool CheckIfIndustryIsAllowed(TileIndex tile, int type, const Town *t) truelight@4300: { belugas@6886: if ((GetIndustrySpec(type)->behaviour & INDUSTRYBEH_TOWN1200_MORE) && t->population < 1200) { truelight@4300: _error_message = STR_029D_CAN_ONLY_BE_BUILT_IN_TOWNS; truelight@4300: return false; truelight@4300: } truelight@4300: belugas@6886: if ((GetIndustrySpec(type)->behaviour & INDUSTRYBEH_ONLY_NEARTOWN) && DistanceMax(t->xy, tile) > 9) { truelight@4300: _error_message = STR_0239_SITE_UNSUITABLE; truelight@4300: return false; truelight@4300: } truelight@4300: truelight@4300: return true; truelight@4300: } truelight@4300: truelight@4300: static bool CheckCanTerraformSurroundingTiles(TileIndex tile, uint height, int internal) truelight@4300: { truelight@4300: int size_x, size_y; truelight@4300: uint curh; truelight@4300: truelight@4300: size_x = 2; truelight@4300: size_y = 2; truelight@4300: truelight@4300: /* Check if we don't leave the map */ truelight@4300: if (TileX(tile) == 0 || TileY(tile) == 0 || GetTileType(tile) == MP_VOID) return false; truelight@4300: truelight@4300: tile += TileDiffXY(-1, -1); truelight@4300: BEGIN_TILE_LOOP(tile_walk, size_x, size_y, tile) { truelight@4300: curh = TileHeight(tile_walk); truelight@4300: /* Is the tile clear? */ truelight@4300: if ((GetTileType(tile_walk) != MP_CLEAR) && (GetTileType(tile_walk) != MP_TREES)) truelight@4300: return false; truelight@4300: truelight@4300: /* Don't allow too big of a change if this is the sub-tile check */ skidd13@8466: if (internal != 0 && Delta(curh, height) > 1) return false; truelight@4300: truelight@4300: /* Different height, so the surrounding tiles of this tile truelight@4300: * has to be correct too (in level, or almost in level) truelight@4300: * else you get a chain-reaction of terraforming. */ truelight@4300: if (internal == 0 && curh != height) { truelight@4300: if (!CheckCanTerraformSurroundingTiles(tile_walk + TileDiffXY(-1, -1), height, internal + 1)) truelight@4300: return false; truelight@4300: } truelight@4300: } END_TILE_LOOP(tile_walk, size_x, size_y, tile); truelight@4300: truelight@4300: return true; truelight@4300: } truelight@4300: truelight@4300: /** truelight@4300: * This function tries to flatten out the land below an industry, without truelight@4300: * damaging the surroundings too much. truelight@4300: */ truelight@4300: static bool CheckIfCanLevelIndustryPlatform(TileIndex tile, uint32 flags, const IndustryTileTable* it, int type) truelight@4300: { truelight@4300: const int MKEND = -0x80; // used for last element in an IndustryTileTable (see build_industry.h) truelight@4300: int max_x = 0; truelight@4300: int max_y = 0; truelight@4300: TileIndex cur_tile; truelight@4300: uint size_x, size_y; truelight@4300: uint h, curh; truelight@4300: truelight@4300: /* Finds dimensions of largest variant of this industry */ truelight@4300: do { belugas@8125: if (it->gfx == 0xFF) continue; // FF been a marquer for a check on clear water, skip it truelight@4300: if (it->ti.x > max_x) max_x = it->ti.x; truelight@4300: if (it->ti.y > max_y) max_y = it->ti.y; truelight@4300: } while ((++it)->ti.x != MKEND); truelight@4300: truelight@4300: /* Remember level height */ truelight@4300: h = TileHeight(tile); truelight@4300: truelight@4300: /* Check that all tiles in area and surrounding are clear truelight@4300: * this determines that there are no obstructing items */ truelight@4300: cur_tile = tile + TileDiffXY(-1, -1); truelight@4300: size_x = max_x + 4; truelight@4300: size_y = max_y + 4; truelight@4300: truelight@4300: /* Check if we don't leave the map */ rubidium@4313: if (TileX(cur_tile) == 0 || TileY(cur_tile) == 0 || TileX(cur_tile) + size_x >= MapMaxX() || TileY(cur_tile) + size_y >= MapMaxY()) return false; truelight@4300: rubidium@8136: /* _current_player is OWNER_NONE for randomly generated industries and in editor, or the player who funded or prospected the industry. rubidium@8136: * Perform terraforming as OWNER_TOWN to disable autoslope. */ rubidium@8136: PlayerID old_player = _current_player; rubidium@8136: _current_player = OWNER_TOWN; rubidium@8136: truelight@4300: BEGIN_TILE_LOOP(tile_walk, size_x, size_y, cur_tile) { truelight@4300: curh = TileHeight(tile_walk); truelight@4300: if (curh != h) { truelight@4300: /* This tile needs terraforming. Check if we can do that without truelight@4300: * damaging the surroundings too much. */ rubidium@8136: if (!CheckCanTerraformSurroundingTiles(tile_walk, h, 0)) { rubidium@8136: _current_player = old_player; rubidium@8136: return false; rubidium@8136: } truelight@4300: /* This is not 100% correct check, but the best we can do without modifying the map. truelight@4300: * What is missing, is if the difference in height is more than 1.. */ rubidium@8136: if (CmdFailed(DoCommand(tile_walk, SLOPE_N, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND))) { rubidium@8136: _current_player = old_player; rubidium@8136: return false; rubidium@8136: } truelight@4300: } truelight@4300: } END_TILE_LOOP(tile_walk, size_x, size_y, cur_tile) truelight@4300: truelight@4300: if (flags & DC_EXEC) { truelight@4300: /* Terraform the land under the industry */ truelight@4300: BEGIN_TILE_LOOP(tile_walk, size_x, size_y, cur_tile) { truelight@4300: curh = TileHeight(tile_walk); truelight@4300: while (curh != h) { truelight@4300: /* We give the terraforming for free here, because we can't calculate truelight@4300: * exact cost in the test-round, and as we all know, that will cause truelight@4300: * a nice assert if they don't match ;) */ rubidium@7976: DoCommand(tile_walk, SLOPE_N, (curh > h) ? 0 : 1, flags, CMD_TERRAFORM_LAND); truelight@4300: curh += (curh > h) ? -1 : 1; truelight@4300: } truelight@4300: } END_TILE_LOOP(tile_walk, size_x, size_y, cur_tile) truelight@4300: } truelight@4300: rubidium@8136: _current_player = old_player; truelight@4300: return true; truelight@4300: } truelight@4300: truelight@4300: belugas@8730: static bool CheckIfFarEnoughFromIndustry(TileIndex tile, int type) truelight@0: { belugas@3669: const IndustrySpec *indspec = GetIndustrySpec(type); belugas@3662: const Industry *i; truelight@0: belugas@3669: if (_patches.same_industry_close && indspec->accepts_cargo[0] == CT_INVALID) belugas@8730: /* Allow primary industries to be placed close to any other industry */ truelight@0: return true; truelight@201: truelight@830: FOR_ALL_INDUSTRIES(i) { belugas@8730: /* Within 14 tiles from another industry is considered close */ belugas@8730: bool in_low_distance = DistanceMax(tile, i->xy) <= 14; belugas@8730: belugas@6527: /* check if an industry that accepts the same goods is nearby */ belugas@8730: if (in_low_distance && belugas@8730: indspec->accepts_cargo[0] != CT_INVALID && // not a primary industry? glx@8141: indspec->accepts_cargo[0] == i->accepts_cargo[0] && ( belugas@8730: /* at least one of those options must be true */ belugas@8730: _game_mode != GM_EDITOR || // editor must not be stopped belugas@8730: !_patches.same_industry_close || belugas@8730: !_patches.multiple_industry_per_town)) { tron@2639: _error_message = STR_INDUSTRY_TOO_CLOSE; tron@2639: return false; tron@2639: } truelight@0: belugas@8730: /* check if there are any conflicting industry types around */ belugas@8730: if ((i->type == indspec->conflicting[0] || belugas@8730: i->type == indspec->conflicting[1] || belugas@8730: i->type == indspec->conflicting[2]) && belugas@8730: in_low_distance) { truelight@0: _error_message = STR_INDUSTRY_TOO_CLOSE; truelight@0: return false; truelight@0: } truelight@0: } truelight@0: return true; truelight@0: } truelight@0: belugas@7941: static void DoCreateNewIndustry(Industry *i, TileIndex tile, int type, const IndustryTileTable *it, byte layout, const Town *t, Owner owner) truelight@0: { belugas@3669: const IndustrySpec *indspec = GetIndustrySpec(type); truelight@0: uint32 r; glx@8142: uint j; truelight@0: truelight@0: i->xy = tile; truelight@0: i->width = i->height = 0; truelight@0: i->type = type; belugas@7320: IncIndustryTypeCount(type); truelight@0: glx@8141: i->produced_cargo[0] = indspec->produced_cargo[0]; glx@8141: i->produced_cargo[1] = indspec->produced_cargo[1]; glx@8141: i->accepts_cargo[0] = indspec->accepts_cargo[0]; glx@8141: i->accepts_cargo[1] = indspec->accepts_cargo[1]; glx@8141: i->accepts_cargo[2] = indspec->accepts_cargo[2]; belugas@3669: i->production_rate[0] = indspec->production_rate[0]; belugas@3669: i->production_rate[1] = indspec->production_rate[1]; truelight@0: glx@8443: /* don't use smooth economy for industries using production related callbacks */ glx@8443: if (_patches.smooth_economy && glx@8443: !(HasBit(indspec->callback_flags, CBM_IND_PRODUCTION_256_TICKS) || HasBit(indspec->callback_flags, CBM_IND_PRODUCTION_CARGO_ARRIVAL)) && // production callbacks glx@8443: !(HasBit(indspec->callback_flags, CBM_IND_MONTHLYPROD_CHANGE) || HasBit(indspec->callback_flags, CBM_IND_PRODUCTION_CHANGE)) // production change callbacks glx@8443: ) { truelight@0: i->production_rate[0] = min((RandomRange(256) + 128) * i->production_rate[0] >> 8 , 255); truelight@0: i->production_rate[1] = min((RandomRange(256) + 128) * i->production_rate[1] >> 8 , 255); truelight@0: } truelight@0: truelight@0: i->town = t; truelight@0: i->owner = owner; truelight@0: tron@2484: r = Random(); rubidium@8358: i->random_color = GB(r, 0, 4); rubidium@8358: i->counter = GB(r, 4, 12); rubidium@8358: i->random = GB(r, 16, 16); rubidium@7661: i->produced_cargo_waiting[0] = 0; rubidium@7661: i->produced_cargo_waiting[1] = 0; rubidium@7661: i->incoming_cargo_waiting[0] = 0; rubidium@7661: i->incoming_cargo_waiting[1] = 0; rubidium@7661: i->incoming_cargo_waiting[2] = 0; rubidium@7315: i->this_month_production[0] = 0; rubidium@7315: i->this_month_production[1] = 0; rubidium@7315: i->this_month_transported[0] = 0; rubidium@7315: i->this_month_transported[1] = 0; rubidium@7315: i->last_month_pct_transported[0] = 0; rubidium@7315: i->last_month_pct_transported[1] = 0; rubidium@7315: i->last_month_transported[0] = 0; rubidium@7315: i->last_month_transported[1] = 0; truelight@0: i->was_cargo_delivered = false; rubidium@4329: i->last_prod_year = _cur_year; rubidium@7315: i->last_month_production[0] = i->production_rate[0] * 8; rubidium@7315: i->last_month_production[1] = i->production_rate[1] * 8; rubidium@7682: i->founder = _current_player; glx@8142: skidd13@8424: if (HasBit(indspec->callback_flags, CBM_IND_DECIDE_COLOUR)) { glx@8146: uint16 res = GetIndustryCallback(CBID_INDUSTRY_DECIDE_COLOUR, 0, 0, i, type, INVALID_TILE); glx@8146: if (res != CALLBACK_FAILED) i->random_color = GB(res, 0, 4); glx@8146: } glx@8146: skidd13@8424: if (HasBit(indspec->callback_flags, CBM_IND_INPUT_CARGO_TYPES)) { glx@8142: for (j = 0; j < lengthof(i->accepts_cargo); j++) i->accepts_cargo[j] = CT_INVALID; glx@8142: for (j = 0; j < lengthof(i->accepts_cargo); j++) { glx@8142: uint16 res = GetIndustryCallback(CBID_INDUSTRY_INPUT_CARGO_TYPES, j, 0, i, type, INVALID_TILE); glx@8142: if (res == CALLBACK_FAILED || GB(res, 0, 8) == CT_INVALID) break; glx@8142: i->accepts_cargo[j] = GetCargoTranslation(GB(res, 0, 8), indspec->grf_prop.grffile); glx@8142: } glx@8142: } glx@8142: skidd13@8424: if (HasBit(indspec->callback_flags, CBM_IND_OUTPUT_CARGO_TYPES)) { glx@8142: for (j = 0; j < lengthof(i->produced_cargo); j++) i->produced_cargo[j] = CT_INVALID; glx@8142: for (j = 0; j < lengthof(i->produced_cargo); j++) { glx@8142: uint16 res = GetIndustryCallback(CBID_INDUSTRY_OUTPUT_CARGO_TYPES, j, 0, i, type, INVALID_TILE); glx@8142: if (res == CALLBACK_FAILED || GB(res, 0, 8) == CT_INVALID) break; glx@8142: i->produced_cargo[j] = GetCargoTranslation(GB(res, 0, 8), indspec->grf_prop.grffile); glx@8142: } glx@8142: } glx@8142: rubidium@7682: i->construction_date = _date; rubidium@7682: i->construction_type = (_game_mode == GM_EDITOR) ? ICT_SCENARIO_EDITOR : rubidium@7682: (_generating_world ? ICT_MAP_GENERATION : ICT_NORMAL_GAMEPLAY); truelight@0: belugas@7941: /* Adding 1 here makes it conform to specs of var44 of varaction2 for industries belugas@7941: * 0 = created prior of newindustries belugas@7941: * else, chosen layout + 1 */ belugas@7941: i->selected_layout = layout + 1; belugas@7941: rubidium@7315: if (!_generating_world) i->last_month_production[0] = i->last_month_production[1] = 0; truelight@201: truelight@0: i->prod_level = 0x10; truelight@0: truelight@0: do { tron@1977: TileIndex cur_tile = tile + ToTileIndexDiff(it->ti); truelight@0: belugas@7969: if (it->gfx != GFX_WATERTILE_SPECIALCHECK) { truelight@0: byte size; truelight@0: tron@909: size = it->ti.x; truelight@0: if (size > i->width) i->width = size; tron@909: size = it->ti.y; truelight@0: if (size > i->height)i->height = size; truelight@0: tron@3491: DoCommand(cur_tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR); truelight@0: rubidium@8615: MakeIndustry(cur_tile, i->index, it->gfx, Random()); rubidium@8349: rubidium@5686: if (_generating_world) { rubidium@5686: SetIndustryConstructionCounter(cur_tile, 3); rubidium@5686: SetIndustryConstructionStage(cur_tile, 2); rubidium@8358: } frosch@8807: frosch@8807: /* it->gfx is stored in the map. But the translated ID cur_gfx is the interesting one */ frosch@8807: IndustryGfx cur_gfx = GetTranslatedIndustryTileID(it->gfx); frosch@8807: const IndustryTileSpec *its = GetIndustryTileSpec(cur_gfx); frosch@8807: if (its->animation_info != 0xFFFF) AddAnimatedTile(cur_tile); truelight@0: } tron@909: } while ((++it)->ti.x != -0x80); truelight@0: truelight@0: i->width++; truelight@0: i->height++; truelight@0: belugas@6886: if (GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_PLANT_ON_BUILT) { truelight@4328: for (j = 0; j != 50; j++) PlantRandomFarmField(i); truelight@0: } truelight@0: _industry_sort_dirty = true; truelight@0: InvalidateWindow(WC_INDUSTRY_DIRECTORY, 0); truelight@0: } truelight@0: belugas@6527: /** Helper function for Build/Fund an industry belugas@6527: * @param tile tile where industry is built belugas@6527: * @param type of industry to build belugas@6527: * @param flags of operations to conduct belugas@6527: * @param indspec pointer to industry specifications rubidium@7703: * @param itspec_index the index of the itsepc to build/fund rubidium@8932: * @param seed random seed (possibly) used by industries belugas@6527: * @return the pointer of the newly created industry, or NULL if it failed belugas@6527: */ rubidium@8932: static Industry *CreateNewIndustryHelper(TileIndex tile, IndustryType type, uint32 flags, const IndustrySpec *indspec, uint itspec_index, uint32 seed) truelight@4300: { rubidium@7703: const IndustryTileTable *it = indspec->table[itspec_index]; rubidium@7708: bool custom_shape_check = false; rubidium@7708: rubidium@8158: if (!CheckIfIndustryTilesAreFree(tile, it, itspec_index, type, &custom_shape_check)) return NULL; rubidium@7708: skidd13@8424: if (HasBit(GetIndustrySpec(type)->callback_flags, CBM_IND_LOCATION)) { rubidium@8932: if (!CheckIfCallBackAllowsCreation(tile, type, itspec_index, seed)) return NULL; rubidium@7708: } else { rubidium@7708: if (!_check_new_industry_procs[indspec->check_proc](tile)) return NULL; rubidium@7703: } truelight@4300: rubidium@8545: if (!custom_shape_check && _patches.land_generator == LG_TERRAGENESIS && _generating_world && !_ignore_restrictions && !CheckIfCanLevelIndustryPlatform(tile, 0, it, type)) return NULL; belugas@8730: if (!CheckIfFarEnoughFromIndustry(tile, type)) return NULL; truelight@4300: rubidium@7703: const Town *t = CheckMultipleIndustryInTown(tile, type); truelight@4300: if (t == NULL) return NULL; truelight@4300: truelight@4300: if (!CheckIfIndustryIsAllowed(tile, type, t)) return NULL; truelight@4300: if (!CheckSuitableIndustryPos(tile)) return NULL; truelight@4300: rubidium@7886: Industry *i = new Industry(tile); truelight@4300: if (i == NULL) return NULL; rubidium@7886: AutoPtrT i_auto_delete = i; truelight@4300: truelight@4300: if (flags & DC_EXEC) { rubidium@7708: if (!custom_shape_check) CheckIfCanLevelIndustryPlatform(tile, DC_EXEC, it, type); belugas@7941: DoCreateNewIndustry(i, tile, type, it, itspec_index, t, OWNER_NONE); rubidium@7886: i_auto_delete.Detach(); truelight@4300: } truelight@4300: truelight@4300: return i; truelight@4300: } truelight@4300: Darkvater@1786: /** Build/Fund an industry tron@3491: * @param tile tile where industry is built belugas@6527: * @param flags of operations to conduct rubidium@8932: * @param p1 various bitstuffed elements rubidium@8932: * - p1 = (bit 0 - 15) - industry type see build_industry.h and see industry.h rubidium@8932: * - p1 = (bit 16 - 31) - first layout to try rubidium@8932: * @param p2 seed to use for variable 8F belugas@6527: * @return index of the newly create industry, or CMD_ERROR if it failed Darkvater@1786: */ rubidium@7439: CommandCost CmdBuildIndustry(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { rubidium@8932: const IndustrySpec *indspec = GetIndustrySpec(GB(p1, 0, 16)); tron@2639: belugas@4965: /* Check if the to-be built/founded industry is available for this climate. */ belugas@7244: if (!indspec->enabled) { belugas@7244: return CMD_ERROR; belugas@7244: } truelight@0: tron@2638: /* If the patch for raw-material industries is not on, you cannot build raw-material industries. rubidium@7681: * Raw material industries are industries that do not accept cargo (at least for now) */ rubidium@7703: if (_game_mode != GM_EDITOR && _patches.raw_industry_construction == 0 && indspec->IsRawIndustry()) { tron@2638: return CMD_ERROR; tron@2638: } truelight@0: rubidium@7703: if (_game_mode != GM_EDITOR && _patches.raw_industry_construction == 2 && indspec->IsRawIndustry()) { rubidium@7673: if (flags & DC_EXEC) { rubidium@7673: /* Prospecting has a chance to fail, however we cannot guarantee that something can rubidium@7673: * be built on the map, so the chance gets lower when the map is fuller, but there rubidium@7673: * is nothing we can really do about that. */ rubidium@7673: if (Random() <= indspec->prospecting_chance) { rubidium@7673: for (int i = 0; i < 5000; i++) { glx@8650: /* We should not have more than one Random() in a function call glx@8650: * because parameter evaluation order is not guaranteed in the c++ standard glx@8650: */ glx@8650: tile = RandomTile(); rubidium@8932: const Industry *ind = CreateNewIndustryHelper(tile, p1, flags, indspec, RandomRange(indspec->num_table), p2); maedhros@7699: if (ind != NULL) { maedhros@7699: SetDParam(0, indspec->name); glx@8251: if (indspec->new_industry_text > STR_LAST_STRINGID) { glx@8251: SetDParam(1, STR_TOWN); glx@8251: SetDParam(2, ind->town->index); glx@8251: } else { glx@8251: SetDParam(1, ind->town->index); glx@8251: } maedhros@7699: AddNewsItem(indspec->new_industry_text, maedhros@7699: NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, NT_OPENCLOSE, 0), ind->xy, 0); maedhros@7699: break; maedhros@7699: } rubidium@7673: } rubidium@7673: } rubidium@7673: } rubidium@7673: } else { glx@8256: int count = indspec->num_table; glx@8256: const IndustryTileTable * const *itt = indspec->table; rubidium@8932: int num = Clamp(GB(p1, 16, 16), 0, count - 1); truelight@0: glx@8244: _error_message = STR_0239_SITE_UNSUITABLE; rubidium@7673: do { glx@8256: if (--count < 0) return CMD_ERROR; glx@8256: if (--num < 0) num = indspec->num_table - 1; rubidium@8158: } while (!CheckIfIndustryTilesAreFree(tile, itt[num], num, p1)); truelight@201: rubidium@8932: if (CreateNewIndustryHelper(tile, p1, flags, indspec, num, p2) == NULL) return CMD_ERROR; rubidium@7673: } rubidium@7673: rubidium@8726: return CommandCost(EXPENSES_OTHER, indspec->GetConstructionCost()); truelight@0: } truelight@0: truelight@0: belugas@3538: Industry *CreateNewIndustry(TileIndex tile, IndustryType type) truelight@0: { truelight@4300: const IndustrySpec *indspec = GetIndustrySpec(type); truelight@0: rubidium@8932: uint32 seed = Random(); rubidium@8932: return CreateNewIndustryHelper(tile, type, DC_EXEC, indspec, RandomRange(indspec->num_table), seed); truelight@0: } truelight@0: belugas@7834: enum { belugas@7834: NB_NUMOFINDUSTRY = 11, belugas@7834: NB_DIFFICULTY_LEVEL = 5, belugas@7834: }; belugas@7834: belugas@7834: static const byte _numof_industry_table[NB_DIFFICULTY_LEVEL][NB_NUMOFINDUSTRY] = { belugas@6527: /* difficulty settings for number of industries */ belugas@3669: {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //none belugas@7056: {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1}, //very low belugas@3669: {0, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5}, //low belugas@3669: {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, //normal belugas@3669: {0, 2, 3, 4, 6, 7, 8, 9, 10, 10, 10}, //high truelight@0: }; truelight@0: belugas@6938: /** This function is the one who really do the creation work belugas@6938: * of random industries during game creation belugas@6938: * @param type IndustryType of the desired industry belugas@6938: * @param amount of industries that need to be built */ belugas@3669: static void PlaceInitialIndustry(IndustryType type, int amount) truelight@0: { belugas@7834: /* We need to bypass the amount given in parameter if it exceeds the maximum dimension of the belugas@7834: * _numof_industry_table. newgrf can specify a big amount */ belugas@7834: int num = (amount > NB_NUMOFINDUSTRY) ? amount : _numof_industry_table[_opt.diff.number_industries][amount]; belugas@7253: const IndustrySpec *ind_spc = GetIndustrySpec(type); ludde@2072: belugas@6938: /* These are always placed next to the coastline, so we scale by the perimeter instead. */ belugas@7253: num = (ind_spc->check_proc == CHECK_REFINERY || ind_spc->check_proc == CHECK_OIL_RIG) ? ScaleByMapSize1D(num) : ScaleByMapSize(num); truelight@110: tron@2639: if (_opt.diff.number_industries != 0) { tron@2498: PlayerID old_player = _current_player; darkvater@266: _current_player = OWNER_NONE; orudge@61: assert(num > 0); truelight@0: truelight@0: do { tron@2639: uint i; tron@2639: truelight@4300: IncreaseGeneratingWorldProgress(GWP_INDUSTRY); truelight@4300: tron@2639: for (i = 0; i < 2000; i++) { tron@2639: if (CreateNewIndustry(RandomTile(), type) != NULL) break; tron@2639: } truelight@110: } while (--num); darkvater@266: darkvater@267: _current_player = old_player; orudge@61: } truelight@0: } truelight@0: belugas@6938: /** This function will create ramdon industries during game creation. belugas@6938: * It will scale the amount of industries by map size as well as difficulty level */ rubidium@6573: void GenerateIndustries() truelight@0: { truelight@4300: uint i = 0; belugas@6938: uint8 chance; belugas@6938: IndustryType it; belugas@6938: const IndustrySpec *ind_spc; truelight@4300: truelight@4300: /* Find the total amount of industries */ belugas@7757: if (_opt.diff.number_industries > 0) { rubidium@7815: for (it = 0; it < NUM_INDUSTRYTYPES; it++) { truelight@4300: belugas@7757: ind_spc = GetIndustrySpec(it); belugas@7757: belugas@7757: if (!CheckIfCallBackAllowsAvailability(it, IACT_MAPGENERATION)) { belugas@7757: ResetIndustryCreationProbility(it); belugas@7757: } belugas@7757: belugas@7244: chance = ind_spc->appear_creation[_opt.landscape]; glx@7758: if (ind_spc->enabled && chance > 0) { belugas@7244: /* once the chance of appearance is determind, it have to be scaled by belugas@7244: * the difficulty level. The "chance" in question is more an index into belugas@7244: * the _numof_industry_table,in fact */ belugas@7834: int num = (chance > NB_NUMOFINDUSTRY) ? chance : _numof_industry_table[_opt.diff.number_industries][chance]; belugas@6938: belugas@7244: /* These are always placed next to the coastline, so we scale by the perimeter instead. */ belugas@7253: num = (ind_spc->check_proc == CHECK_REFINERY || ind_spc->check_proc == CHECK_OIL_RIG) ? ScaleByMapSize1D(num) : ScaleByMapSize(num); belugas@7244: i += num; belugas@7244: } truelight@4300: } belugas@6938: } truelight@4300: truelight@4300: SetGeneratingWorldProgress(GWP_INDUSTRY, i); truelight@0: belugas@7757: if (_opt.diff.number_industries > 0) { rubidium@7815: for (it = 0; it < NUM_INDUSTRYTYPES; it++) { belugas@7757: /* Once the number of industries has been determined, let's really create them. belugas@7757: * The test for chance allows us to try create industries that are available only belugas@7757: * for this landscape. belugas@7757: * @todo : Do we really have to pass chance as un-scaled value, since we've already belugas@7757: * processed that scaling above? No, don't think so. Will find a way. */ belugas@7757: ind_spc = GetIndustrySpec(it); belugas@7757: if (ind_spc->enabled) { belugas@7757: chance = ind_spc->appear_creation[_opt.landscape]; belugas@7757: if (chance > 0) PlaceInitialIndustry(it, chance); belugas@7757: } belugas@7244: } belugas@7757: } truelight@0: } truelight@0: truelight@0: static void UpdateIndustryStatistics(Industry *i) truelight@0: { truelight@0: byte pct; belugas@6963: bool refresh = false; truelight@201: glx@8141: for (byte j = 0; j < lengthof(i->produced_cargo); j++) { glx@8141: if (i->produced_cargo[j] != CT_INVALID) { belugas@7132: pct = 0; rubidium@7315: if (i->this_month_production[j] != 0) { belugas@7132: i->last_prod_year = _cur_year; rubidium@7315: pct = min(i->this_month_transported[j] * 256 / i->this_month_production[j], 255); belugas@7132: } rubidium@7315: i->last_month_pct_transported[j] = pct; truelight@0: rubidium@7315: i->last_month_production[j] = i->this_month_production[j]; rubidium@7315: i->this_month_production[j] = 0; truelight@0: rubidium@7315: i->last_month_transported[j] = i->this_month_transported[j]; rubidium@7315: i->this_month_transported[j] = 0; belugas@7132: refresh = true; belugas@7132: } truelight@0: } truelight@0: glx@8109: if (refresh) InvalidateWindow(WC_INDUSTRY_VIEW, i->index); truelight@0: } truelight@0: belugas@6927: /** Simple helper that will collect data for the generation of industries */ belugas@6927: struct ProbabilityHelper { belugas@6927: uint16 prob; ///< probability belugas@6927: IndustryType ind; ///< industry id correcponding belugas@6927: }; belugas@6927: belugas@6915: /** belugas@6915: * Try to create a random industry, during gameplay belugas@6915: */ belugas@6915: static void MaybeNewIndustry(void) belugas@6915: { belugas@6915: Industry *ind; //will receive the industry's creation pointer belugas@6915: IndustryType rndtype, j; // Loop controlers belugas@6915: const IndustrySpec *ind_spc; belugas@6915: uint num = 0; belugas@6989: ProbabilityHelper cumulative_probs[NUM_INDUSTRYTYPES]; // probability collector belugas@6915: uint16 probability_max = 0; truelight@0: belugas@6915: /* Generate a list of all possible industries that can be built. */ belugas@6989: for (j = 0; j < NUM_INDUSTRYTYPES; j++) { rubidium@8319: ind_spc = GetIndustrySpec(j); rubidium@8319: byte chance = ind_spc->appear_ingame[_opt.landscape]; rubidium@8319: rubidium@8319: if (!ind_spc->enabled || chance == 0) continue; truelight@0: belugas@7757: /* If there is no Callback CBID_INDUSTRY_AVAILABLE or if this one did anot failed, belugas@7757: * and if appearing chance for this landscape is above 0, this industry can be chosen */ rubidium@8319: if (CheckIfCallBackAllowsAvailability(j, IACT_RANDOMCREATION)) { belugas@6915: probability_max += chance; belugas@6927: /* adds the result for this industry */ belugas@6927: cumulative_probs[num].ind = j; belugas@6927: cumulative_probs[num++].prob = probability_max; belugas@6915: } belugas@6915: } belugas@6915: belugas@6927: /* Find a random type, with maximum being what has been evaluate above*/ belugas@6915: rndtype = RandomRange(probability_max); belugas@6989: for (j = 0; j < NUM_INDUSTRYTYPES; j++) { belugas@6927: /* and choose the index of the industry that matches as close as possible this random type */ belugas@6927: if (cumulative_probs[j].prob >= rndtype) break; belugas@6915: } belugas@6915: belugas@6927: ind_spc = GetIndustrySpec(cumulative_probs[j].ind); belugas@6915: /* Check if it is allowed */ belugas@6886: if ((ind_spc->behaviour & INDUSTRYBEH_BEFORE_1950) && _cur_year > 1950) return; belugas@6886: if ((ind_spc->behaviour & INDUSTRYBEH_AFTER_1960) && _cur_year < 1960) return; truelight@0: belugas@6927: /* try to create 2000 times this industry */ belugas@6915: num = 2000; tron@2639: for (;;) { belugas@6927: ind = CreateNewIndustry(RandomTile(), cumulative_probs[j].ind); belugas@6915: if (ind != NULL) break; belugas@6915: if (--num == 0) return; truelight@0: } truelight@0: belugas@6592: SetDParam(0, ind_spc->name); glx@8251: if (ind_spc->new_industry_text > STR_LAST_STRINGID) { glx@8251: SetDParam(1, STR_TOWN); glx@8251: SetDParam(2, ind->town->index); glx@8251: } else { glx@8251: SetDParam(1, ind->town->index); glx@8251: } belugas@6592: AddNewsItem(ind_spc->new_industry_text, rubidium@6987: NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, NT_OPENCLOSE, 0), ind->xy, 0); truelight@0: } truelight@0: glx@8109: /** glx@8109: * Protects an industry from closure if the appropriate flags and conditions are met glx@8109: * INDUSTRYBEH_CANCLOSE_LASTINSTANCE must be set (which, by default, it is not) and the glx@8109: * count of industries of this type must one (or lower) in order to be protected glx@8109: * against closure. glx@8109: * @param type IndustryType been queried glx@8109: * @result true if protection is on, false otherwise (except for oil wells) glx@8109: */ glx@8109: static bool CheckIndustryCloseDownProtection(IndustryType type) truelight@0: { glx@8109: const IndustrySpec *indspec = GetIndustrySpec(type); glx@8109: glx@8109: /* oil wells (or the industries with that flag set) are always allowed to closedown */ glx@8109: if (indspec->behaviour & INDUSTRYBEH_DONT_INCR_PROD && _opt.landscape == LT_TEMPERATE) return false; rubidium@8317: return (indspec->behaviour & INDUSTRYBEH_CANCLOSE_LASTINSTANCE) == 0 && GetIndustryTypeCount(type) <= 1; glx@8109: } glx@8109: rubidium@8387: /** rubidium@8387: * Can given cargo type be accepted or produced by the industry? rubidium@8387: * @param cargo: Cargo type rubidium@8387: * @param ind: Industry rubidium@8387: * @param *c_accepts: Pointer to boolean for acceptance of cargo rubidium@8387: * @param *c_produces: Pointer to boolean for production of cargo rubidium@8387: * @return: \c *c_accepts is set when industry accepts the cargo type, rubidium@8387: * \c *c_produces is set when the industry produces the cargo type rubidium@8387: */ rubidium@8387: static void CanCargoServiceIndustry(CargoID cargo, Industry *ind, bool *c_accepts, bool *c_produces) rubidium@8387: { rubidium@8387: const IndustrySpec *indspec = GetIndustrySpec(ind->type); rubidium@8387: rubidium@8387: /* Check for acceptance of cargo */ rubidium@8387: for (uint j = 0; j < lengthof(ind->accepts_cargo) && ind->accepts_cargo[j] != CT_INVALID; j++) { rubidium@8387: if (cargo == ind->accepts_cargo[j]) { skidd13@8424: if (HasBit(indspec->callback_flags, CBM_IND_REFUSE_CARGO)) { rubidium@8387: uint16 res = GetIndustryCallback(CBID_INDUSTRY_REFUSE_CARGO, rubidium@8387: 0, GetReverseCargoTranslation(cargo, indspec->grf_prop.grffile), rubidium@8387: ind, ind->type, ind->xy); rubidium@8387: if (res == 0) continue; rubidium@8387: } rubidium@8387: *c_accepts = true; rubidium@8387: break; rubidium@8387: } rubidium@8387: } rubidium@8387: rubidium@8387: /* Check for produced cargo */ rubidium@8387: for (uint j = 0; j < lengthof(ind->produced_cargo) && ind->produced_cargo[j] != CT_INVALID; j++) { rubidium@8387: if (cargo == ind->produced_cargo[j]) { rubidium@8387: *c_produces = true; rubidium@8387: break; rubidium@8387: } rubidium@8387: } rubidium@8387: } rubidium@8387: rubidium@8387: /** rubidium@8387: * Compute who can service the industry. rubidium@8387: * rubidium@8387: * Here, 'can service' means that he/she has trains and stations close enough rubidium@8387: * to the industry with the right cargo type and the right orders (ie has the rubidium@8387: * technical means). rubidium@8387: * rubidium@8387: * @param ind: Industry being investigated. rubidium@8387: * rubidium@8387: * @return: 0 if nobody can service the industry, 2 if the local player can rubidium@8387: * service the industry, and 1 otherwise (only competitors can service the rubidium@8387: * industry) rubidium@8387: */ rubidium@8387: int WhoCanServiceIndustry(Industry* ind) rubidium@8387: { rubidium@8387: /* Find all stations within reach of the industry */ rubidium@8387: StationSet stations = FindStationsAroundIndustryTile(ind->xy, ind->width, ind->height); rubidium@8387: rubidium@8387: if (stations.size() == 0) return 0; // No stations found at all => nobody services rubidium@8387: rubidium@8387: const Vehicle *v; rubidium@8387: int result = 0; rubidium@8387: FOR_ALL_VEHICLES(v) { rubidium@8387: /* Is it worthwhile to try this vehicle? */ rubidium@8387: if (v->owner != _local_player && result != 0) continue; rubidium@8387: rubidium@8387: /* Check whether it accepts the right kind of cargo */ rubidium@8387: bool c_accepts = false; rubidium@8387: bool c_produces = false; rubidium@8387: if (v->type == VEH_TRAIN && IsFrontEngine(v)) { rubidium@8387: const Vehicle *u = v; rubidium@8387: BEGIN_ENUM_WAGONS(u) rubidium@8387: CanCargoServiceIndustry(u->cargo_type, ind, &c_accepts, &c_produces); rubidium@8387: END_ENUM_WAGONS(u) rubidium@8387: } else if (v->type == VEH_ROAD || v->type == VEH_SHIP || v->type == VEH_AIRCRAFT) { rubidium@8387: CanCargoServiceIndustry(v->cargo_type, ind, &c_accepts, &c_produces); rubidium@8387: } else { rubidium@8387: continue; rubidium@8387: } rubidium@8387: if (!c_accepts && !c_produces) continue; // Wrong cargo rubidium@8387: rubidium@8387: /* Check orders of the vehicle. rubidium@8387: * We cannot check the first of shared orders only, since the first vehicle in such a chain rubidium@8387: * may have a different cargo type. rubidium@8387: */ rubidium@8387: const Order *o; rubidium@8387: FOR_VEHICLE_ORDERS(v, o) { rubidium@8798: if (o->type == OT_GOTO_STATION && !HasBit(o->flags, OF_TRANSFER)) { rubidium@8387: /* Vehicle visits a station to load or unload */ rubidium@8387: Station *st = GetStation(o->dest); rubidium@8387: if (!st->IsValid()) continue; rubidium@8387: rubidium@8387: /* Same cargo produced by industry is dropped here => not serviced by vehicle v */ rubidium@8798: if (HasBit(o->flags, OF_UNLOAD) && !c_accepts) break; rubidium@8387: rubidium@8387: if (stations.find(st) != stations.end()) { rubidium@8387: if (v->owner == _local_player) return 2; // Player services industry rubidium@8387: result = 1; // Competitor services industry rubidium@8387: } rubidium@8387: } rubidium@8387: } rubidium@8387: } rubidium@8387: return result; rubidium@8387: } rubidium@8387: rubidium@8387: /** rubidium@8387: * Report news that industry production has changed significantly rubidium@8387: * rubidium@8387: * @param ind: Industry with changed production rubidium@8387: * @param type: Cargo type that has changed rubidium@8387: * @param percent: Percentage of change (>0 means increase, <0 means decrease) rubidium@8387: */ rubidium@8387: static void ReportNewsProductionChangeIndustry(Industry *ind, CargoID type, int percent) rubidium@8387: { rubidium@8387: NewsType nt; rubidium@8387: rubidium@8387: switch (WhoCanServiceIndustry(ind)) { rubidium@8387: case 0: nt = NT_INDUSTRY_NOBODY; break; rubidium@8387: case 1: nt = NT_INDUSTRY_OTHER; break; rubidium@8387: case 2: nt = NT_INDUSTRY_PLAYER; break; rubidium@8387: default: NOT_REACHED(); break; rubidium@8387: } rubidium@8387: SetDParam(2, abs(percent)); rubidium@8387: SetDParam(0, GetCargo(type)->name); rubidium@8387: SetDParam(1, ind->index); rubidium@8387: AddNewsItem( rubidium@8387: percent >= 0 ? STR_INDUSTRY_PROD_GOUP : STR_INDUSTRY_PROD_GODOWN, rubidium@8387: NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, nt, 0), rubidium@8387: ind->xy + TileDiffXY(1, 1), 0 rubidium@8387: ); rubidium@8387: } rubidium@8387: belugas@8441: enum { belugas@8441: PERCENT_TRANSPORTED_60 = 153, belugas@8699: PERCENT_TRANSPORTED_80 = 204, belugas@8441: }; belugas@8441: glx@8109: /** Change industry production or do closure glx@8109: * @param i Industry for which changes are performed glx@8109: * @param monthly true if it's the monthly call, false if it's the random call glx@8109: */ glx@8109: static void ChangeIndustryProduction(Industry *i, bool monthly) glx@8109: { glx@8111: extern StringID MapGRFStringID(uint32 grfid, StringID str); truelight@1320: StringID str = STR_NULL; glx@8109: bool closeit = false; glx@8109: const IndustrySpec *indspec = GetIndustrySpec(i->type); glx@8111: bool standard = true; glx@8111: bool suppress_message = false; glx@8443: /* don't use smooth economy for industries using production related callbacks */ glx@8443: bool smooth_economy = _patches.smooth_economy && glx@8443: !(HasBit(indspec->callback_flags, CBM_IND_PRODUCTION_256_TICKS) || HasBit(indspec->callback_flags, CBM_IND_PRODUCTION_CARGO_ARRIVAL)) && // production callbacks glx@8443: !(HasBit(indspec->callback_flags, CBM_IND_MONTHLYPROD_CHANGE) || HasBit(indspec->callback_flags, CBM_IND_PRODUCTION_CHANGE)); // production change callbacks glx@8111: byte div = 0; glx@8111: byte mul = 0; belugas@8472: int8 increment = 0; truelight@201: skidd13@8424: if (HasBit(indspec->callback_flags, monthly ? CBM_IND_MONTHLYPROD_CHANGE : CBM_IND_PRODUCTION_CHANGE)) { glx@8111: uint16 res = GetIndustryCallback(monthly ? CBID_INDUSTRY_MONTHLYPROD_CHANGE : CBID_INDUSTRY_PRODUCTION_CHANGE, 0, Random(), i, i->type, i->xy); glx@8443: standard = false; glx@8443: monthly = false; // smooth economy is disabled so we need to fake random industry production change to allow 'use standard' result glx@8111: if (res != CALLBACK_FAILED) { skidd13@8424: suppress_message = HasBit(res, 7); glx@8111: /* Get the custom message if any */ skidd13@8424: if (HasBit(res, 8)) str = MapGRFStringID(indspec->grf_prop.grffile->grfid, GB(GetRegister(0x100), 0, 16)); glx@8111: res = GB(res, 0, 4); glx@8111: switch(res) { glx@8111: default: NOT_REACHED(); glx@8111: case 0x0: break; // Do nothing, but show the custom message if any glx@8111: case 0x1: div = 1; break; // Halve industry production. If production reaches the quarter of the default, the industry is closed instead. glx@8111: case 0x2: mul = 1; break; // Double industry production if it hasn't reached eight times of the original yet. glx@8111: case 0x3: closeit = true; break; // The industry announces imminent closure, and is physically removed from the map next month. glx@8111: case 0x4: standard = true; break; // Do the standard random production change as if this industry was a primary one. glx@8111: case 0x5: case 0x6: case 0x7: // Divide production by 4, 8, 16 glx@8440: case 0x8: div = res - 0x3; break; // Divide production by 32 glx@8111: case 0x9: case 0xA: case 0xB: // Multiply production by 4, 8, 16 glx@8440: case 0xC: mul = res - 0x7; break; // Multiply production by 32 belugas@8472: case 0xD: // decrement production belugas@8472: case 0xE: // increment production belugas@8472: increment = res == 0x0D ? -1 : 1; belugas@8472: break; glx@8111: } glx@8111: } glx@8111: } truelight@1320: glx@8172: if (standard && monthly != smooth_economy) return; glx@8111: glx@8111: if (standard && indspec->life_type == INDUSTRYLIFE_BLACK_HOLE) return; glx@8111: glx@8111: if (standard && (indspec->life_type & (INDUSTRYLIFE_ORGANIC | INDUSTRYLIFE_EXTRACTIVE)) != 0) { rubidium@7640: /* decrease or increase */ glx@8109: bool only_decrease = (indspec->behaviour & INDUSTRYBEH_DONT_INCR_PROD) && _opt.landscape == LT_TEMPERATE; rubidium@7640: glx@8172: if (smooth_economy) { glx@8109: closeit = true; glx@8141: for (byte j = 0; j < 2 && i->produced_cargo[j] != CT_INVALID; j++){ belugas@8699: uint32 r = Random(); glx@8109: int old_prod, new_prod, percent; belugas@8699: /* If over 60% is transported, mult is 1, else mult is -1. */ belugas@8441: int mult = (i->last_month_pct_transported[j] > PERCENT_TRANSPORTED_60) ? 1 : -1; rubidium@7640: glx@8109: new_prod = old_prod = i->production_rate[j]; glx@8109: belugas@8699: /* For industries with only_decrease flags (temperate terrain Oil Wells), belugas@8699: * the multiplier will always be -1 so they will only decrease. */ maedhros@8468: if (only_decrease) { maedhros@8468: mult = -1; belugas@8699: /* For normal industries, if over 60% is transported, 33% chance for decrease. belugas@8699: * Bonus for very high station ratings (over 80%): 16% chance for decrease. */ belugas@8699: } else if (Chance16I(1, ((i->last_month_pct_transported[j] > PERCENT_TRANSPORTED_80) ? 6 : 3), r)) { maedhros@8468: mult *= -1; maedhros@8468: } belugas@8441: belugas@8699: /* 4.5% chance for 3-23% (or 1 unit for very low productions) production change, belugas@8699: * determined by mult value. If mult = 1 prod. increases, else (-1) it decreases. */ belugas@8699: if (Chance16I(1, 22, r >> 16)) { belugas@8441: new_prod += mult * (max(((RandomRange(50) + 10) * old_prod) >> 8, 1U)); rubidium@7640: } glx@8109: belugas@8441: /* Prevent production to overflow or Oil Rig passengers to be over-"produced" */ skidd13@8418: new_prod = Clamp(new_prod, 1, 255); belugas@8441: belugas@8441: if (((indspec->behaviour & INDUSTRYBEH_BUILT_ONWATER) != 0) && j == 1) belugas@8441: new_prod = Clamp(new_prod, 0, 16); belugas@8441: glx@8109: /* Do not stop closing the industry when it has the lowest possible production rate */ glx@8109: if (new_prod == old_prod && old_prod > 1) { glx@8109: closeit = false; glx@8109: continue; glx@8109: } glx@8109: glx@8109: percent = (old_prod == 0) ? 100 : (new_prod * 100 / old_prod - 100); glx@8109: i->production_rate[j] = new_prod; glx@8109: glx@8109: /* Close the industry when it has the lowest possible production rate */ glx@8109: if (new_prod > 1) closeit = false; glx@8109: rubidium@8387: if (abs(percent) >= 10) { rubidium@8387: ReportNewsProductionChangeIndustry(i, i->produced_cargo[j], percent); glx@8109: } glx@8109: } glx@8109: } else { skidd13@8463: if (only_decrease || Chance16(1, 3)) { belugas@8699: /* If more than 60% transported, 66% chance of increase, else 33% chance of increase */ skidd13@8463: if (!only_decrease && (i->last_month_pct_transported[0] > PERCENT_TRANSPORTED_60) != Chance16(1, 3)) { glx@8111: mul = 1; // Increase production truelight@1320: } else { glx@8111: div = 1; // Decrease production truelight@0: } truelight@0: } rubidium@7640: } rubidium@7640: } glx@8109: glx@8111: if (standard && indspec->life_type & INDUSTRYLIFE_PROCESSING) { skidd13@8463: if ( (byte)(_cur_year - i->last_prod_year) >= 5 && Chance16(1, smooth_economy ? 180 : 2)) { glx@8109: closeit = true; rubidium@7640: } truelight@1320: } truelight@1320: glx@8111: /* Increase if needed */ glx@8111: while (mul-- != 0 && i->prod_level < 0x80) { glx@8111: i->prod_level <<= 1; glx@8111: i->production_rate[0] = min(i->production_rate[0] * 2, 0xFF); glx@8111: i->production_rate[1] = min(i->production_rate[1] * 2, 0xFF); glx@8111: if (str == STR_NULL) str = indspec->production_up_text; glx@8111: } glx@8111: glx@8111: /* Decrease if needed */ glx@8111: while (div-- != 0 && !closeit) { glx@8111: if (i->prod_level == 4) { glx@8111: closeit = true; glx@8111: } else { glx@8111: i->prod_level >>= 1; glx@8111: i->production_rate[0] = (i->production_rate[0] + 1) >> 1; glx@8111: i->production_rate[1] = (i->production_rate[1] + 1) >> 1; glx@8111: if (str == STR_NULL) str = indspec->production_down_text; glx@8111: } glx@8111: } glx@8111: belugas@8902: /* Increase or Decreasing the production level if needed */ belugas@8472: if (increment != 0) { belugas@8903: if (increment < 0 && i->prod_level == 4) { belugas@8902: closeit = true; belugas@8902: } else { belugas@8902: i->prod_level = ClampU(i->prod_level + increment, 4, 0x80); belugas@8902: } belugas@8472: } belugas@8472: glx@8109: /* Close if needed and allowed */ glx@8109: if (closeit && !CheckIndustryCloseDownProtection(i->type)) { glx@8109: i->prod_level = 0; glx@8109: str = indspec->closure_text; glx@8109: } glx@8109: glx@8111: if (!suppress_message && str != STR_NULL) { rubidium@8387: NewsType nt; rubidium@8387: /* Compute news category */ rubidium@8387: if (closeit) { rubidium@8387: nt = NT_OPENCLOSE; rubidium@8387: } else { rubidium@8387: switch (WhoCanServiceIndustry(i)) { rubidium@8387: case 0: nt = NT_INDUSTRY_NOBODY; break; rubidium@8387: case 1: nt = NT_INDUSTRY_OTHER; break; rubidium@8387: case 2: nt = NT_INDUSTRY_PLAYER; break; rubidium@8387: default: NOT_REACHED(); break; rubidium@8387: } rubidium@8387: } rubidium@8387: /* Set parameters of news string */ glx@8251: if (str > STR_LAST_STRINGID) { glx@8251: SetDParam(0, STR_TOWN); glx@8251: SetDParam(1, i->town->index); glx@8251: SetDParam(2, indspec->name); rubidium@8366: } else if (closeit) { rubidium@8366: SetDParam(0, STR_INDUSTRY_FORMAT); rubidium@8366: SetDParam(1, i->town->index); rubidium@8366: SetDParam(2, indspec->name); glx@8251: } else { glx@8251: SetDParam(0, i->index); glx@8251: } rubidium@8387: /* and report the news to the user */ glx@8109: AddNewsItem(str, rubidium@8387: NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, nt, 0), glx@8109: i->xy + TileDiffXY(1, 1), 0); truelight@0: } truelight@0: } truelight@0: rubidium@6573: void IndustryMonthlyLoop() truelight@0: { truelight@0: Industry *i; tron@2498: PlayerID old_player = _current_player; darkvater@266: _current_player = OWNER_NONE; truelight@0: truelight@830: FOR_ALL_INDUSTRIES(i) { truelight@4346: UpdateIndustryStatistics(i); glx@8109: if (i->prod_level == 0) { glx@8109: delete i; glx@8109: } else { glx@8109: ChangeIndustryProduction(i, true); glx@8109: } truelight@0: } truelight@0: truelight@1267: /* 3% chance that we start a new industry */ skidd13@8463: if (Chance16(3, 100)) { belugas@6915: MaybeNewIndustry(); glx@8109: } else { truelight@4356: i = GetRandomIndustry(); glx@8109: if (i != NULL) ChangeIndustryProduction(i, false); truelight@0: } darkvater@266: darkvater@266: _current_player = old_player; darkvater@266: belugas@6527: /* production-change */ truelight@0: _industry_sort_dirty = true; truelight@0: InvalidateWindow(WC_INDUSTRY_DIRECTORY, 0); truelight@0: } truelight@0: truelight@0: rubidium@6573: void InitializeIndustries() truelight@0: { rubidium@7897: _Industry_pool.CleanPool(); rubidium@7897: _Industry_pool.AddBlockToPool(); truelight@919: belugas@7335: ResetIndustryCounts(); truelight@0: _industry_sort_dirty = true; rubidium@4587: _industry_sound_tile = 0; truelight@0: } truelight@0: rubidium@7681: bool IndustrySpec::IsRawIndustry() const rubidium@7681: { rubidium@7681: /* Lumber mills are extractive/organic, but can always be built like a non-raw industry */ rubidium@7681: return (this->life_type & (INDUSTRYLIFE_EXTRACTIVE | INDUSTRYLIFE_ORGANIC)) != 0 && rubidium@7681: (this->behaviour & INDUSTRYBEH_CUT_TREES) == 0; rubidium@7681: } rubidium@7681: rubidium@7681: Money IndustrySpec::GetConstructionCost() const rubidium@7681: { rubidium@7681: return (_price.build_industry * rubidium@7681: (_patches.raw_industry_construction == 1 && this->IsRawIndustry() ? rubidium@7681: this->raw_industry_cost_multiplier : rubidium@7681: this->cost_multiplier rubidium@7681: )) >> 8; rubidium@7681: } rubidium@7681: belugas@8474: Money IndustrySpec::GetRemovalCost() const belugas@8474: { belugas@8474: return (_price.remove_house * this->removal_cost_multiplier) >> 8; belugas@8474: } belugas@8474: rubidium@7990: static CommandCost TerraformTile_Industry(TileIndex tile, uint32 flags, uint z_new, Slope tileh_new) rubidium@7990: { rubidium@8078: if (AutoslopeEnabled()) { rubidium@8078: /* We imitate here TTDP's behaviour: rubidium@8078: * - Both new and old slope must not be steep. rubidium@8078: * - TileMaxZ must not be changed. rubidium@8078: * - Allow autoslope by default. rubidium@8078: * - Disallow autoslope if callback succeeds and returns non-zero. rubidium@8078: */ rubidium@8078: Slope tileh_old = GetTileSlope(tile, NULL); rubidium@8078: /* TileMaxZ must not be changed. Slopes must not be steep. */ rubidium@8078: if (!IsSteepSlope(tileh_old) && !IsSteepSlope(tileh_new) && (GetTileMaxZ(tile) == z_new + GetSlopeMaxZ(tileh_new))) { rubidium@8078: const IndustryGfx gfx = GetIndustryGfx(tile); rubidium@8078: const IndustryTileSpec *itspec = GetIndustryTileSpec(gfx); rubidium@8078: rubidium@8078: /* Call callback 3C 'disable autosloping for industry tiles'. */ skidd13@8424: if (HasBit(itspec->callback_flags, CBM_INDT_AUTOSLOPE)) { rubidium@8078: /* If the callback fails, allow autoslope. */ rubidium@8078: uint16 res = GetIndustryTileCallback(CBID_INDUSTRY_AUTOSLOPE, 0, 0, gfx, GetIndustryByTile(tile), tile); rubidium@8726: if ((res == 0) || (res == CALLBACK_FAILED)) return CommandCost(EXPENSES_CONSTRUCTION, _price.terraform); rubidium@8078: } else { rubidium@8387: /* allow autoslope */ rubidium@8726: return CommandCost(EXPENSES_CONSTRUCTION, _price.terraform); rubidium@8078: } rubidium@8078: } rubidium@8078: } rubidium@8133: return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); rubidium@7990: } rubidium@7681: rubidium@5838: extern const TileTypeProcs _tile_type_industry_procs = { rubidium@4344: DrawTile_Industry, /* draw_tile_proc */ rubidium@4344: GetSlopeZ_Industry, /* get_slope_z_proc */ rubidium@4344: ClearTile_Industry, /* clear_tile_proc */ rubidium@4434: GetAcceptedCargo_Industry, /* get_accepted_cargo_proc */ rubidium@4344: GetTileDesc_Industry, /* get_tile_desc_proc */ rubidium@4344: GetTileTrackStatus_Industry, /* get_tile_track_status_proc */ rubidium@4344: ClickTile_Industry, /* click_tile_proc */ rubidium@4344: AnimateTile_Industry, /* animate_tile_proc */ rubidium@4344: TileLoop_Industry, /* tile_loop_proc */ rubidium@4344: ChangeTileOwner_Industry, /* change_tile_owner_proc */ rubidium@4344: GetProducedCargo_Industry, /* get_produced_cargo_proc */ rubidium@4344: NULL, /* vehicle_enter_tile_proc */ rubidium@7831: GetFoundation_Industry, /* get_foundation_proc */ rubidium@7990: TerraformTile_Industry, /* terraform_tile_proc */ truelight@0: }; truelight@0: Darkvater@1881: static const SaveLoad _industry_desc[] = { rubidium@7315: SLE_CONDVAR(Industry, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), rubidium@7315: SLE_CONDVAR(Industry, xy, SLE_UINT32, 6, SL_MAX_VERSION), rubidium@7315: SLE_VAR(Industry, width, SLE_UINT8), rubidium@7315: SLE_VAR(Industry, height, SLE_UINT8), rubidium@7315: SLE_REF(Industry, town, REF_TOWN), rubidium@8855: SLE_CONDNULL( 2, 0, 60), ///< used to be industry's produced_cargo glx@8141: SLE_CONDARR(Industry, produced_cargo, SLE_UINT8, 2, 78, SL_MAX_VERSION), rubidium@7661: SLE_CONDARR(Industry, incoming_cargo_waiting, SLE_UINT16, 3, 70, SL_MAX_VERSION), rubidium@7661: SLE_ARR(Industry, produced_cargo_waiting, SLE_UINT16, 2), rubidium@7315: SLE_ARR(Industry, production_rate, SLE_UINT8, 2), rubidium@8855: SLE_CONDNULL( 3, 0, 60), ///< used to be industry's accepts_cargo glx@8141: SLE_CONDARR(Industry, accepts_cargo, SLE_UINT8, 3, 78, SL_MAX_VERSION), rubidium@7315: SLE_VAR(Industry, prod_level, SLE_UINT8), rubidium@7315: SLE_ARR(Industry, this_month_production, SLE_UINT16, 2), rubidium@7315: SLE_ARR(Industry, this_month_transported, SLE_UINT16, 2), rubidium@7315: SLE_ARR(Industry, last_month_pct_transported, SLE_UINT8, 2), rubidium@7315: SLE_ARR(Industry, last_month_production, SLE_UINT16, 2), rubidium@7315: SLE_ARR(Industry, last_month_transported, SLE_UINT16, 2), truelight@0: rubidium@7315: SLE_VAR(Industry, counter, SLE_UINT16), truelight@0: rubidium@7315: SLE_VAR(Industry, type, SLE_UINT8), rubidium@7315: SLE_VAR(Industry, owner, SLE_UINT8), rubidium@7315: SLE_VAR(Industry, random_color, SLE_UINT8), rubidium@7315: SLE_CONDVAR(Industry, last_prod_year, SLE_FILE_U8 | SLE_VAR_I32, 0, 30), rubidium@7315: SLE_CONDVAR(Industry, last_prod_year, SLE_INT32, 31, SL_MAX_VERSION), rubidium@7315: SLE_VAR(Industry, was_cargo_delivered, SLE_UINT8), truelight@0: rubidium@8018: SLE_CONDVAR(Industry, founder, SLE_UINT8, 70, SL_MAX_VERSION), rubidium@7682: SLE_CONDVAR(Industry, construction_date, SLE_INT32, 70, SL_MAX_VERSION), rubidium@7682: SLE_CONDVAR(Industry, construction_type, SLE_UINT8, 70, SL_MAX_VERSION), rubidium@7682: SLE_CONDVAR(Industry, last_cargo_accepted_at, SLE_INT32, 70, SL_MAX_VERSION), belugas@7941: SLE_CONDVAR(Industry, selected_layout, SLE_UINT8, 73, SL_MAX_VERSION), rubidium@7682: rubidium@8106: SLE_CONDARRX(cpp_offsetof(Industry, psa) + cpp_offsetof(Industry::PersistentStorage, storage), SLE_UINT32, 16, 76, SL_MAX_VERSION), rubidium@8106: rubidium@8356: SLE_CONDVAR(Industry, random_triggers, SLE_UINT8, 82, SL_MAX_VERSION), rubidium@8356: SLE_CONDVAR(Industry, random, SLE_UINT16, 82, SL_MAX_VERSION), rubidium@8356: belugas@6527: /* reserve extra space in savegame here. (currently 32 bytes) */ Darkvater@3222: SLE_CONDNULL(32, 2, SL_MAX_VERSION), truelight@0: truelight@0: SLE_END() truelight@0: }; truelight@0: rubidium@6573: static void Save_INDY() truelight@0: { truelight@0: Industry *ind; truelight@919: belugas@7784: /* Write the industries */ truelight@830: FOR_ALL_INDUSTRIES(ind) { truelight@4346: SlSetArrayIndex(ind->index); truelight@4346: SlObject(ind, _industry_desc); truelight@0: } truelight@0: } truelight@0: belugas@7786: /* Save and load the mapping between the industry/tile id on the map, and the grf file belugas@7786: * it came from. */ belugas@7786: static const SaveLoad _industries_id_mapping_desc[] = { belugas@7786: SLE_VAR(EntityIDMapping, grfid, SLE_UINT32), belugas@7786: SLE_VAR(EntityIDMapping, entity_id, SLE_UINT8), belugas@7786: SLE_VAR(EntityIDMapping, substitute_id, SLE_UINT8), belugas@7786: SLE_END() belugas@7786: }; belugas@7786: belugas@7786: static void Save_IIDS() belugas@7786: { belugas@7786: uint i; belugas@7786: uint j = _industry_mngr.GetMaxMapping(); belugas@7786: belugas@7786: for (i = 0; i < j; i++) { belugas@7786: SlSetArrayIndex(i); belugas@7786: SlObject(&_industry_mngr.mapping_ID[i], _industries_id_mapping_desc); belugas@7786: } belugas@7786: } belugas@7786: belugas@7786: static void Save_TIDS() belugas@7786: { belugas@7786: uint i; belugas@7786: uint j = _industile_mngr.GetMaxMapping(); belugas@7786: belugas@7786: for (i = 0; i < j; i++) { belugas@7786: SlSetArrayIndex(i); belugas@7786: SlObject(&_industile_mngr.mapping_ID[i], _industries_id_mapping_desc); belugas@7786: } belugas@7786: } belugas@7786: rubidium@6573: static void Load_INDY() truelight@0: { truelight@0: int index; truelight@1267: belugas@7335: ResetIndustryCounts(); truelight@1267: truelight@0: while ((index = SlIterateArray()) != -1) { rubidium@7886: Industry *i = new (index) Industry(); truelight@919: SlObject(i, _industry_desc); belugas@7320: IncIndustryTypeCount(i->type); truelight@0: } truelight@0: } truelight@0: belugas@7786: static void Load_IIDS() belugas@7786: { belugas@7786: int index; belugas@7786: uint max_id; belugas@7786: belugas@7786: /* clear the current mapping stored. belugas@7786: * This will create the manager if ever it is not yet done */ belugas@7786: _industry_mngr.ResetMapping(); belugas@7786: belugas@7786: /* get boundary for the temporary map loader NUM_INDUSTRYTYPES? */ belugas@7786: max_id = _industry_mngr.GetMaxMapping(); belugas@7786: belugas@7786: while ((index = SlIterateArray()) != -1) { belugas@7786: if ((uint)index >= max_id) break; belugas@7786: SlObject(&_industry_mngr.mapping_ID[index], _industries_id_mapping_desc); belugas@7786: } belugas@7786: } belugas@7786: belugas@7786: static void Load_TIDS() belugas@7786: { belugas@7786: int index; belugas@7786: uint max_id; belugas@7786: belugas@7786: /* clear the current mapping stored. belugas@7786: * This will create the manager if ever it is not yet done */ belugas@7786: _industile_mngr.ResetMapping(); belugas@7786: belugas@7786: /* get boundary for the temporary map loader NUM_INDUSTILES? */ belugas@7788: max_id = _industile_mngr.GetMaxMapping(); belugas@7786: belugas@7786: while ((index = SlIterateArray()) != -1) { belugas@7786: if ((uint)index >= max_id) break; belugas@7786: SlObject(&_industile_mngr.mapping_ID[index], _industries_id_mapping_desc); belugas@7786: } belugas@7786: } belugas@7786: rubidium@5838: extern const ChunkHandler _industry_chunk_handlers[] = { belugas@7786: { 'INDY', Save_INDY, Load_INDY, CH_ARRAY}, belugas@7786: { 'IIDS', Save_IIDS, Load_IIDS, CH_ARRAY}, belugas@7786: { 'TIDS', Save_TIDS, Load_TIDS, CH_ARRAY | CH_LAST}, truelight@0: };