tron@2186: /* $Id$ */ tron@2186: rubidium@9111: /** @file industry_cmd.cpp Handling of industry tiles. */ belugas@6201: 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@7891: #include "train.h" maedhros@6343: #include "landscape.h" rubidium@8225: #include "viewport_func.h" rubidium@8116: #include "command_func.h" truelight@0: #include "industry.h" truelight@0: #include "town.h" rubidium@8763: #include "news_func.h" truelight@0: #include "saveload.h" tron@2159: #include "variables.h" rubidium@8965: #include "cheat_func.h" truelight@4300: #include "genworld.h" belugas@5056: #include "water_map.h" belugas@5909: #include "tree_map.h" peter1138@6091: #include "cargotype.h" glx@7615: #include "newgrf.h" belugas@7293: #include "newgrf_commons.h" rubidium@7168: #include "newgrf_industries.h" belugas@6970: #include "newgrf_industrytiles.h" belugas@6970: #include "newgrf_callbacks.h" rubidium@7582: #include "autoslope.h" belugas@7849: #include "transparency.h" rubidium@7948: #include "water.h" rubidium@8114: #include "strings_func.h" rubidium@8119: #include "tile_cmd.h" rubidium@8131: #include "functions.h" rubidium@8131: #include "window_func.h" rubidium@8140: #include "date_func.h" rubidium@8144: #include "vehicle_func.h" rubidium@8157: #include "sound_func.h" rubidium@8785: #include "station_base.h" smatz@8847: #include "oldpool_func.h" rubidium@9005: #include "animated_tile_func.h" rubidium@9009: #include "effectvehicle_func.h" truelight@0: rubidium@8264: #include "table/strings.h" rubidium@8264: #include "table/sprites.h" rubidium@8264: #include "table/industry_land.h" rubidium@8264: #include "table/build_industry.h" rubidium@8264: 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@6824: int _total_industries; //general counter belugas@6824: uint16 _industry_counts[NUM_INDUSTRYTYPES]; // Number of industries per type ingame belugas@6824: belugas@6743: IndustrySpec _industry_specs[NUM_INDUSTRYTYPES]; belugas@6743: IndustryTileSpec _industry_tile_specs[NUM_INDUSTRYTILES]; belugas@6743: belugas@6743: /** This function initialize the spec arrays of both belugas@6743: * industry and industry tiles. belugas@6743: * It adjusts the enabling of the industry too, based on climate availability. belugas@6743: * This will allow for clearer testings */ belugas@6743: void ResetIndustries() belugas@6743: { belugas@6743: memset(&_industry_specs, 0, sizeof(_industry_specs)); belugas@6743: memcpy(&_industry_specs, &_origin_industry_specs, sizeof(_origin_industry_specs)); belugas@6743: belugas@6743: /* once performed, enable only the current climate industries */ belugas@6743: for (IndustryType i = 0; i < NUM_INDUSTRYTYPES; i++) { rubidium@7654: _industry_specs[i].enabled = i < NEW_INDUSTRYOFFSET && rubidium@9413: HasBit(_origin_industry_specs[i].climate_availability, _settings_game.game_creation.landscape); belugas@6743: } belugas@6743: belugas@6743: memset(&_industry_tile_specs, 0, sizeof(_industry_tile_specs)); belugas@6743: memcpy(&_industry_tile_specs, &_origin_industry_tile_specs, sizeof(_origin_industry_tile_specs)); belugas@7666: belugas@7666: /* Reset any overrides that have been set. */ belugas@7666: _industile_mngr.ResetOverride(); belugas@7666: _industry_mngr.ResetOverride(); belugas@6743: } belugas@6743: belugas@7261: void ResetIndustryCreationProbility(IndustryType type) belugas@7261: { belugas@7261: assert(type < INVALID_INDUSTRYTYPE); rubidium@9413: _industry_specs[type].appear_creation[_settings_game.game_creation.landscape] = 0; belugas@7261: } belugas@7261: rubidium@7390: 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@6639: const Industry *ind = GetIndustryByTile(tile); rubidium@7390: 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@6493: * @pre thistype < NUM_INDUSTRYTYPES belugas@6201: * @return a pointer to the corresponding industry spec belugas@3662: **/ belugas@3689: const IndustrySpec *GetIndustrySpec(IndustryType thistype) belugas@3662: { belugas@6493: assert(thistype < NUM_INDUSTRYTYPES); belugas@3662: return &_industry_specs[thistype]; belugas@3662: } belugas@3662: belugas@6201: /** belugas@6201: * Accessor for array _industry_tile_specs. belugas@6201: * This will ensure at once : proper access and belugas@6201: * not allowing modifications of it. belugas@7288: * @param gfx of industrytile (which is the index in _industry_tile_specs) belugas@6749: * @pre gfx < INVALID_INDUSTRYTILE belugas@6201: * @return a pointer to the corresponding industrytile spec belugas@6201: **/ belugas@7462: const IndustryTileSpec *GetIndustryTileSpec(IndustryGfx gfx) belugas@6092: { belugas@6749: assert(gfx < INVALID_INDUSTRYTILE); belugas@7462: return &_industry_tile_specs[gfx]; belugas@6092: } belugas@6092: rubidium@7390: Industry::~Industry() truelight@4403: { rubidium@7420: if (CleaningPool()) return; rubidium@7420: rubidium@7390: /* Industry can also be destroyed when not fully initialized. rubidium@7390: * This means that we do not have to clear tiles either. */ rubidium@7390: if (this->width == 0) { rubidium@7390: this->xy = 0; rubidium@7390: return; rubidium@7390: } rubidium@7390: rubidium@7390: BEGIN_TILE_LOOP(tile_cur, this->width, this->height, this->xy); truelight@4403: if (IsTileType(tile_cur, MP_INDUSTRY)) { rubidium@7390: if (GetIndustryIndex(tile_cur) == this->index) { frosch@9718: /* MakeWaterKeepingClass() can also handle 'land' */ frosch@9718: MakeWaterKeepingClass(tile_cur, OWNER_NONE); truelight@4403: } truelight@4403: } else if (IsTileType(tile_cur, MP_STATION) && IsOilRig(tile_cur)) { truelight@4403: DeleteOilRig(tile_cur); truelight@4403: } rubidium@7390: END_TILE_LOOP(tile_cur, this->width, this->height, this->xy); truelight@4403: rubidium@7390: if (GetIndustrySpec(this->type)->behaviour & INDUSTRYBEH_PLANT_FIELDS) { truelight@4403: /* Remove the farmland and convert it to regular tiles over time. */ rubidium@7390: 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@7390: GetIndustryIndexOfField(tile_cur) == this->index) { truelight@4403: SetIndustryIndexOfField(tile_cur, INVALID_INDUSTRY); truelight@4403: } rubidium@7390: } END_TILE_LOOP(tile_cur, 42, 42, this->xy - TileDiff(21, 21)) truelight@4403: } truelight@4403: rubidium@7390: DecIndustryTypeCount(this->type); belugas@6824: rubidium@7390: DeleteSubsidyWithIndustry(this->index); rubidium@7390: DeleteWindowById(WC_INDUSTRY_VIEW, this->index); glx@9306: InvalidateWindowData(WC_INDUSTRY_DIRECTORY, 0, 0); rubidium@7390: this->xy = 0; truelight@4403: } truelight@4403: belugas@3290: static void IndustryDrawSugarMine(const TileInfo *ti) truelight@0: { belugas@6519: const DrawIndustryAnimationStruct *d; truelight@0: tron@3321: if (!IsIndustryCompleted(ti->tile)) return; truelight@201: rubidium@5435: d = &_draw_industry_spec1[GetIndustryAnimationState(ti->tile)]; truelight@0: peter1138@5668: AddChildSpriteScreen(SPR_IT_SUGAR_MINE_SIEVE + d->image_1, PAL_NONE, d->x, 0); truelight@0: belugas@6519: if (d->image_2 != 0) { belugas@6519: AddChildSpriteScreen(SPR_IT_SUGAR_MINE_CLOUDS + d->image_2 - 1, PAL_NONE, 8, 41); belugas@6519: } truelight@0: belugas@6519: if (d->image_3 != 0) { belugas@6519: AddChildSpriteScreen(SPR_IT_SUGAR_MINE_PILE + d->image_3 - 1, PAL_NONE, belugas@6519: _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@6519: uint8 x = 0; truelight@201: tron@3321: if (IsIndustryCompleted(ti->tile)) { belugas@6519: x = _industry_anim_offs_toffee[GetIndustryAnimationState(ti->tile)]; belugas@6519: if (x == 0xFF) truelight@0: x = 0; truelight@0: } truelight@0: peter1138@5668: AddChildSpriteScreen(SPR_IT_TOFFEE_QUARRY_SHOVEL, PAL_NONE, 22 - x, 24 + x); peter1138@5668: 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@6519: AddChildSpriteScreen(SPR_IT_BUBBLE_GENERATOR_BUBBLE, PAL_NONE, 5, _industry_anim_offs_bubbles[GetIndustryAnimationState(ti->tile)]); tron@2639: } else { peter1138@5668: 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@6519: const DrawIndustryAnimationStruct *d; truelight@0: belugas@6519: d = &_industry_anim_offs_toys[GetIndustryAnimationState(ti->tile)]; truelight@0: truelight@0: if (d->image_1 != 0xFF) { belugas@6519: 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@5668: AddChildSpriteScreen(SPR_IT_TOY_FACTORY_ROBOT, PAL_NONE, 16 - d->image_2 * 2, 100 + d->image_2); truelight@0: } truelight@0: peter1138@5668: AddChildSpriteScreen(SPR_IT_TOY_FACTORY_STAMP, PAL_NONE, 7, d->image_3); peter1138@5668: 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@6519: uint8 image = GetIndustryAnimationState(ti->tile); tron@3321: truelight@0: if (image != 0 && image < 7) { belugas@3290: AddChildSpriteScreen(image + SPR_IT_POWER_PLANT_TRANSFORMERS, peter1138@5668: PAL_NONE, belugas@6519: _coal_plant_sparks[image - 1].x, belugas@6519: _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@6970: IndustryGfx gfx = GetIndustryGfx(ti->tile); belugas@6970: Industry *ind = GetIndustryByTile(ti->tile); belugas@6970: const IndustryTileSpec *indts = GetIndustryTileSpec(gfx); belugas@3654: const DrawBuildingsTileStruct *dits; peter1138@5668: SpriteID image; peter1138@5668: SpriteID pal; truelight@0: belugas@6970: /* Retrieve pointer to the draw industry tile struct */ belugas@6970: if (gfx >= NEW_INDUSTRYTILEOFFSET) { belugas@6970: /* Draw the tile using the specialized method of newgrf industrytile. belugas@6970: * DrawNewIndustry will return false if ever the resolver could not belugas@6970: * find any sprite to display. So in this case, we will jump on the belugas@6970: * substitute gfx instead. */ belugas@6970: if (indts->grf_prop.spritegroup != NULL && DrawNewIndustryTile(ti, ind, gfx, indts)) { belugas@6970: return; belugas@6970: } else { belugas@6970: /* No sprite group (or no valid one) found, meaning no graphics associated. belugas@6970: * Use the substitute one instead */ belugas@7461: if (indts->grf_prop.subst_id != INVALID_INDUSTRYTILE) { belugas@7461: gfx = indts->grf_prop.subst_id; belugas@7461: /* And point the industrytile spec accordingly */ belugas@7461: indts = GetIndustryTileSpec(gfx); belugas@7461: } belugas@6970: } belugas@6970: } truelight@0: belugas@6970: dits = &_industry_draw_tile_data[gfx << 2 | (indts->anim_state ? belugas@7291: GetIndustryAnimationState(ti->tile) & INDUSTRY_COMPLETED : rubidium@5436: GetIndustryConstructionStage(ti->tile))]; truelight@0: peter1138@5668: image = dits->ground.sprite; skidd13@7928: if (HasBit(image, PALETTE_MODIFIER_COLOR) && dits->ground.pal == PAL_NONE) { peter1138@5668: pal = GENERAL_SPRITE_COLOR(ind->random_color); peter1138@5668: } else { peter1138@5668: pal = dits->ground.pal; peter1138@5668: } truelight@0: rubidium@7335: /* DrawFoundation() modifes ti->z and ti->tileh */ rubidium@7335: if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, FOUNDATION_LEVELED); truelight@0: frosch@9718: /* If the ground sprite is the default flat water sprite, draw also canal/river borders. frosch@9718: * Do not do this if the tile's WaterClass is 'land'. */ frosch@9718: if (image == SPR_FLAT_WATER_TILE && IsIndustryTileOnWater(ti->tile)) { frosch@9718: DrawWaterClassGround(ti); frosch@9718: } else { frosch@9718: DrawGroundSprite(image, pal); frosch@9718: } rubidium@7035: smatz@8806: /* If industries are transparent and invisible, do not draw the upper part */ smatz@8806: if (IsInvisibilitySet(TO_INDUSTRIES)) return; smatz@8806: truelight@0: /* Add industry on top of the ground? */ peter1138@5668: image = dits->building.sprite; tron@2639: if (image != 0) { rubidium@7333: AddSortableSpriteToDraw(image, skidd13@7928: (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@7577: dits->width, rubidium@7577: dits->height, truelight@0: dits->dz, rubidium@7335: ti->z, belugas@7849: IsTransparencySet(TO_INDUSTRIES)); truelight@0: belugas@7849: 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@7335: static Foundation GetFoundation_Industry(TileIndex tile, Slope tileh) tron@2548: { rubidium@9433: IndustryGfx gfx = GetIndustryGfx(tile); rubidium@9433: rubidium@9433: /* For NewGRF industry tiles we might not be drawing a foundation. We need to frosch@9509: * account for this, as other structures should rubidium@9433: * draw the wall of the foundation in this case. rubidium@9433: */ rubidium@9433: if (gfx >= NEW_INDUSTRYTILEOFFSET) { rubidium@9433: const IndustryTileSpec *indts = GetIndustryTileSpec(gfx); rubidium@9433: if (indts->grf_prop.spritegroup != NULL && HasBit(indts->callback_flags, CBM_INDT_DRAW_FOUNDATIONS)) { rubidium@9433: uint32 callback_res = GetIndustryTileCallback(CBID_INDUSTRY_DRAW_FOUNDATIONS, 0, 0, gfx, GetIndustryByTile(tile), tile); rubidium@9433: if (callback_res == 0) return FOUNDATION_NONE; rubidium@9433: } rubidium@9433: } rubidium@7335: return FlatteningFoundation(tileh); dominik@39: } dominik@39: tron@1977: static void GetAcceptedCargo_Industry(TileIndex tile, AcceptedCargo ac) truelight@0: { rubidium@7199: IndustryGfx gfx = GetIndustryGfx(tile); rubidium@7199: const IndustryTileSpec *itspec = GetIndustryTileSpec(gfx); rubidium@7199: rubidium@7199: /* When we have to use a callback, we put our data in the next two variables */ rubidium@7199: CargoID raw_accepts_cargo[lengthof(itspec->accepts_cargo)]; rubidium@7199: uint8 raw_acceptance[lengthof(itspec->acceptance)]; rubidium@7199: rubidium@7199: /* And then these will always point to a same sized array with the required data */ rubidium@7199: const CargoID *accepts_cargo = itspec->accepts_cargo; rubidium@7199: const uint8 *acceptance = itspec->acceptance; rubidium@7199: skidd13@7928: if (HasBit(itspec->callback_flags, CBM_INDT_ACCEPT_CARGO)) { rubidium@7199: uint16 res = GetIndustryTileCallback(CBID_INDTILE_ACCEPT_CARGO, 0, 0, gfx, GetIndustryByTile(tile), tile); rubidium@7199: if (res != CALLBACK_FAILED) { rubidium@7199: accepts_cargo = raw_accepts_cargo; rubidium@7199: 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@7199: } rubidium@7199: } rubidium@7199: skidd13@7928: if (HasBit(itspec->callback_flags, CBM_INDT_CARGO_ACCEPTANCE)) { rubidium@7199: uint16 res = GetIndustryTileCallback(CBID_INDTILE_CARGO_ACCEPTANCE, 0, 0, gfx, GetIndustryByTile(tile), tile); rubidium@7199: if (res != CALLBACK_FAILED) { rubidium@7199: acceptance = raw_acceptance; rubidium@7199: for (uint i = 0; i < lengthof(itspec->accepts_cargo); i++) raw_acceptance[i] = GB(res, i * 4, 4); rubidium@7199: } rubidium@7199: } truelight@0: belugas@6736: for (byte i = 0; i < lengthof(itspec->accepts_cargo); i++) { rubidium@7199: CargoID a = accepts_cargo[i]; rubidium@7655: /* Only set the value once. */ rubidium@7655: if (a != CT_INVALID && ac[a] == 0) ac[a] = acceptance[i]; belugas@6736: } truelight@0: } truelight@0: tron@1977: static void GetTileDesc_Industry(TileIndex tile, TileDesc *td) truelight@0: { belugas@3662: const Industry *i = GetIndustryByTile(tile); smatz@9714: const IndustrySpec *is = GetIndustrySpec(i->type); truelight@0: frosch@9322: td->owner[0] = i->owner; smatz@9714: td->str = is->name; tron@3321: if (!IsIndustryCompleted(tile)) { tron@534: SetDParamX(td->dparam, 0, td->str); truelight@0: td->str = STR_2058_UNDER_CONSTRUCTION; truelight@0: } smatz@9714: smatz@9714: if (is->grf_prop.grffile != NULL) { smatz@9714: td->grf = GetGRFConfig(is->grf_prop.grffile->grfid)->name; smatz@9714: } truelight@0: } truelight@0: rubidium@6943: static CommandCost ClearTile_Industry(TileIndex tile, byte flags) truelight@0: { belugas@3662: Industry *i = GetIndustryByTile(tile); belugas@6390: 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: */ rubidium@10207: if ((_current_company != OWNER_WATER && _game_mode != GM_EDITOR && darkvater@149: !_cheats.magic_bulldozer.value) || rubidium@7637: ((flags & DC_AUTO) != 0) || rubidium@10207: (_current_company == OWNER_WATER && rubidium@9938: ((indspec->behaviour & INDUSTRYBEH_BUILT_ONWATER) || rubidium@9938: HasBit(GetIndustryTileSpec(GetIndustryGfx(tile))->slopes_refused, 5)))) { belugas@6390: SetDParam(0, indspec->name); truelight@0: return_cmd_error(STR_4800_IN_THE_WAY); truelight@0: } truelight@0: rubidium@7390: if (flags & DC_EXEC) delete i; rubidium@8230: 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@7799: bool moved_cargo = false; truelight@0: rubidium@7799: for (uint j = 0; j < lengthof(i->produced_cargo_waiting); j++) { rubidium@7799: uint cw = min(i->produced_cargo_waiting[j], 255); rubidium@7799: if (cw > indspec->minimal_cargo && i->produced_cargo[j] != CT_INVALID) { rubidium@7799: i->produced_cargo_waiting[j] -= cw; tron@3331: rubidium@7799: /* fluctuating economy? */ rubidium@7799: if (_economy.fluct <= 0) cw = (cw + 1) / 2; rubidium@7799: rubidium@7799: i->this_month_production[j] += cw; rubidium@7799: rubidium@7799: uint am = MoveGoodsToStation(i->xy, i->width, i->height, i->produced_cargo[j], cw); rubidium@7799: i->this_month_transported[j] += am; rubidium@7799: rubidium@7799: moved_cargo |= (am != 0); truelight@0: } truelight@0: } truelight@0: rubidium@7799: if (moved_cargo && !StartStopIndustryTileAnimation(i, IAT_INDUSTRY_DISTRIBUTES_CARGO)) { rubidium@7799: uint newgfx = GetIndustryTileSpec(GetIndustryGfx(tile))->anim_production; truelight@201: rubidium@7799: if (newgfx != INDUSTRYTILE_NOANIM) { rubidium@7799: ResetIndustryConstructionStage(tile); rubidium@7799: SetIndustryCompleted(tile, true); rubidium@7799: SetIndustryGfx(tile, newgfx); rubidium@7799: MarkTileDirtyByTile(tile); rubidium@7799: } truelight@0: } truelight@0: } truelight@0: truelight@0: tron@1977: static void AnimateTile_Industry(TileIndex tile) truelight@0: { tron@3331: byte m; rubidium@7229: IndustryGfx gfx = GetIndustryGfx(tile); truelight@0: rubidium@7229: if (GetIndustryTileSpec(gfx)->animation_info != 0xFFFF) { rubidium@7229: AnimateNewIndustryTile(tile); rubidium@7229: return; rubidium@7229: } rubidium@7229: rubidium@7229: switch (gfx) { belugas@3545: case GFX_SUGAR_MINE_SIEVE: truelight@0: if ((_tick_counter & 1) == 0) { rubidium@5435: 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@5435: 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@5435: m = GetIndustryAnimationState(tile); truelight@201: belugas@6519: 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@5435: SetIndustryAnimationState(tile, m); truelight@0: truelight@0: MarkTileDirtyByTile(tile); truelight@0: } truelight@0: break; truelight@0: belugas@3545: case GFX_BUBBLE_CATCHER: rubidium@5435: if ((_tick_counter & 1) == 0) { rubidium@5435: m = GetIndustryAnimationState(tile); truelight@0: truelight@0: if (++m >= 40) { truelight@0: m = 0; truelight@0: DeleteAnimatedTile(tile); truelight@0: } rubidium@5435: SetIndustryAnimationState(tile, m); truelight@0: truelight@0: MarkTileDirtyByTile(tile); truelight@0: } truelight@0: break; truelight@0: belugas@6201: /* Sparks on a coal plant */ belugas@3545: case GFX_POWERPLANT_SPARKS: truelight@0: if ((_tick_counter & 3) == 0) { rubidium@5435: m = GetIndustryAnimationState(tile); rubidium@5435: if (m == 6) { rubidium@5435: SetIndustryAnimationState(tile, 0); truelight@0: DeleteAnimatedTile(tile); truelight@0: } else { rubidium@5435: 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@5435: m = GetIndustryAnimationState(tile) + 1; truelight@0: belugas@6468: switch (m) { belugas@6468: case 1: SndPlayTileFx(SND_2C_MACHINERY, tile); break; belugas@6468: case 23: SndPlayTileFx(SND_2B_COMEDY_HIT, tile); break; belugas@6468: case 28: SndPlayTileFx(SND_2A_EXTRACT_AND_POP, tile); break; belugas@6468: default: belugas@6468: if (m >= 50) { belugas@6468: int n = GetIndustryAnimationLoop(tile) + 1; belugas@6468: m = 0; belugas@6468: if (n >= 8) { belugas@6468: n = 0; belugas@6468: DeleteAnimatedTile(tile); belugas@6468: } belugas@6468: SetIndustryAnimationLoop(tile, n); belugas@6468: } truelight@0: } truelight@0: rubidium@5435: SetIndustryAnimationState(tile, m); truelight@0: MarkTileDirtyByTile(tile); truelight@0: } truelight@0: break; truelight@0: rubidium@5434: case GFX_PLASTIC_FOUNTAIN_ANIMATED_1: case GFX_PLASTIC_FOUNTAIN_ANIMATED_2: rubidium@5434: case GFX_PLASTIC_FOUNTAIN_ANIMATED_3: case GFX_PLASTIC_FOUNTAIN_ANIMATED_4: rubidium@5434: case GFX_PLASTIC_FOUNTAIN_ANIMATED_5: case GFX_PLASTIC_FOUNTAIN_ANIMATED_6: rubidium@5434: 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@7967: bool b = Chance16(1, 7); belugas@3499: IndustryGfx gfx = GetIndustryGfx(tile); tron@3331: rubidium@5435: 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@5435: SetIndustryConstructionStage(tile, 3); truelight@201: DeleteAnimatedTile(tile); truelight@0: } else { rubidium@5435: 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@5435: m = GetIndustryAnimationState(tile); rubidium@5435: if (!(m & 0x40)) { rubidium@5436: 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@5435: m = (GetIndustryAnimationState(tile) + 1) | 0x40; truelight@0: if (m > 0xC2) m = 0xC0; rubidium@5435: 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@5435: m = (GetIndustryAnimationState(tile) & 0xBF) - 1; truelight@0: if (m < 0x80) m = 0x82; rubidium@5435: SetIndustryAnimationState(tile, m); truelight@0: MarkTileDirtyByTile(tile); truelight@0: } truelight@0: } break; truelight@0: } truelight@0: } truelight@0: rubidium@9009: static void CreateChimneySmoke(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@7229: StartStopIndustryTileAnimation(tile, IAT_CONSTRUCTION_STATE_CHANGE); belugas@7291: if (stage == INDUSTRY_COMPLETED) SetIndustryCompleted(tile, true); truelight@201: truelight@0: MarkTileDirtyByTile(tile); truelight@0: tron@3321: if (!IsIndustryCompleted(tile)) return; truelight@0: rubidium@7229: IndustryGfx gfx = GetIndustryGfx(tile); rubidium@7229: if (gfx >= NEW_INDUSTRYTILEOFFSET) { rubidium@7853: /* New industries are already animated on construction. */ rubidium@7229: return; rubidium@7229: } rubidium@7229: rubidium@7229: switch (gfx) { rubidium@4583: case GFX_POWERPLANT_CHIMNEY: rubidium@9009: CreateChimneySmoke(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@5435: SetIndustryAnimationState(tile, 0); belugas@3538: SetIndustryAnimationLoop(tile, 0); truelight@0: break; truelight@0: rubidium@5434: case GFX_PLASTIC_FOUNTAIN_ANIMATED_1: case GFX_PLASTIC_FOUNTAIN_ANIMATED_2: rubidium@5434: case GFX_PLASTIC_FOUNTAIN_ANIMATED_3: case GFX_PLASTIC_FOUNTAIN_ANIMATED_4: rubidium@5434: case GFX_PLASTIC_FOUNTAIN_ANIMATED_5: case GFX_PLASTIC_FOUNTAIN_ANIMATED_6: rubidium@5434: 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; rubidium@9009: static const int8 _bubble_spawn_location[3][4] = { rubidium@9009: { 11, 0, -4, -14 }, rubidium@9009: { -4, -10, -4, 1 }, rubidium@9009: { 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( rubidium@9009: TileX(tile) * TILE_SIZE + _bubble_spawn_location[0][dir], rubidium@9009: TileY(tile) * TILE_SIZE + _bubble_spawn_location[1][dir], rubidium@9009: _bubble_spawn_location[2][dir], tron@1359: EV_BUBBLE truelight@0: ); truelight@0: rubidium@9008: if (v != NULL) v->u.effect.animation_substate = dir; truelight@0: } truelight@0: tron@1977: static void TileLoop_Industry(TileIndex tile) truelight@0: { belugas@3499: IndustryGfx newgfx; rubidium@5435: IndustryGfx gfx; truelight@0: frosch@9718: if (IsIndustryTileOnWater(tile)) TileLoop_Water(tile); frosch@9718: rubidium@7860: TriggerIndustryTile(tile, INDTILE_TRIGGER_TILE_LOOP); rubidium@7860: 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@7229: if (StartStopIndustryTileAnimation(tile, IAT_TILELOOP)) return; rubidium@7229: belugas@6305: newgfx = GetIndustryTileSpec(GetIndustryGfx(tile))->anim_next; belugas@6743: 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@5435: gfx = GetIndustryGfx(tile); truelight@0: rubidium@5435: switch (gfx) { 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@7967: if (!(_tick_counter & 0x400) && Chance16(1, 2)) { rubidium@5435: switch (gfx) { rubidium@5435: case GFX_COAL_MINE_TOWER_NOT_ANIMATED: gfx = GFX_COAL_MINE_TOWER_ANIMATED; break; rubidium@5435: case GFX_COPPER_MINE_TOWER_NOT_ANIMATED: gfx = GFX_COPPER_MINE_TOWER_ANIMATED; break; rubidium@5435: case GFX_GOLD_MINE_TOWER_NOT_ANIMATED: gfx = GFX_GOLD_MINE_TOWER_ANIMATED; break; rubidium@5435: } rubidium@5435: SetIndustryGfx(tile, gfx); rubidium@5435: SetIndustryAnimationState(tile, 0x80); rubidium@5435: AddAnimatedTile(tile); rubidium@5435: } truelight@0: break; truelight@0: rubidium@4583: case GFX_OILWELL_NOT_ANIMATED: skidd13@7967: if (Chance16(1, 6)) { rubidium@5435: SetIndustryGfx(tile, GFX_OILWELL_ANIMATED_1); rubidium@5435: SetIndustryAnimationState(tile, 0); rubidium@5435: AddAnimatedTile(tile); rubidium@5435: } 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@5435: if (!(_tick_counter & 0x400)) { rubidium@5435: switch (gfx) { rubidium@5435: case GFX_COAL_MINE_TOWER_ANIMATED: gfx = GFX_COAL_MINE_TOWER_NOT_ANIMATED; break; rubidium@5435: case GFX_COPPER_MINE_TOWER_ANIMATED: gfx = GFX_COPPER_MINE_TOWER_NOT_ANIMATED; break; rubidium@5435: case GFX_GOLD_MINE_TOWER_ANIMATED: gfx = GFX_GOLD_MINE_TOWER_NOT_ANIMATED; break; rubidium@5435: } rubidium@5435: SetIndustryGfx(tile, gfx); rubidium@5435: SetIndustryCompleted(tile, true); rubidium@5435: SetIndustryConstructionStage(tile, 3); rubidium@5435: DeleteAnimatedTile(tile); rubidium@5435: } rubidium@4583: break; rubidium@4583: rubidium@4583: case GFX_POWERPLANT_SPARKS: skidd13@7967: 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@7967: 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: frosch@8616: static TrackStatus GetTileTrackStatus_Industry(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side) truelight@0: { truelight@0: return 0; truelight@0: } truelight@0: Darkvater@3344: static void GetProducedCargo_Industry(TileIndex tile, CargoID *b) truelight@0: { glx@7645: 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: rubidium@10207: static void ChangeTileOwner_Industry(TileIndex tile, Owner old_owner, Owner new_owner) truelight@0: { rubidium@7186: /* If the founder merges, the industry was created by the merged company */ rubidium@7186: Industry *i = GetIndustryByTile(tile); rubidium@10207: if (i->founder == old_owner) i->founder = (new_owner == INVALID_OWNER) ? OWNER_NONE : new_owner; 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@5684: case MP_CLEAR: return IsClearGround(tile, CLEAR_FIELDS) || IsClearGround(tile, CLEAR_SNOW) || IsClearGround(tile, CLEAR_DESERT); frosch@8459: 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@5684: case MP_CLEAR: return IsClearGround(tile, CLEAR_SNOW) || IsClearGround(tile, CLEAR_DESERT); frosch@8459: 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@5587: byte or_ = type; truelight@201: skidd13@7967: if (or_ == 1 && Chance16(1, 7)) or_ = 2; truelight@0: tron@3157: if (direction == AXIS_X) { rubidium@5587: SetFenceSE(tile, or_); tron@3157: } else { rubidium@5587: 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: rubidium@9413: if (_settings_game.game_creation.landscape == LT_ARCTIC) { maedhros@6343: 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; rubidium@9413: if (_settings_game.game_creation.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; rubidium@9413: if (_settings_game.game_creation.landscape != LT_ARCTIC && _settings_game.game_creation.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 frosch@9592: * @param user_data that is passed by the caller. In this case, nothing belugas@6201: * @return the result of the test belugas@5118: */ frosch@9592: static bool SearchLumberMillTrees(TileIndex tile, void *user_data) belugas@5118: { belugas@5909: if (IsTileType(tile, MP_TREES) && GetTreeGrowth(tile) > 2) { ///< 3 and up means all fully grown trees rubidium@10207: CompanyID old_company = _current_company; belugas@5118: /* found a tree */ belugas@5118: rubidium@10207: _current_company = 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: rubidium@10207: _current_company = old_company; 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: frosch@9592: if (CircularTileSearch(&tile, 40, SearchLumberMillTrees, NULL)) ///< 40x40 tiles to search rubidium@7165: 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@6655: const IndustrySpec *indsp = GetIndustrySpec(i->type); truelight@0: truelight@0: /* play a sound? */ truelight@0: if ((i->counter & 0x3F) == 0) { skidd13@7967: if (Chance16R(1, 14, r) && (num = indsp->number_of_sounds) != 0) { truelight@0: SndPlayTileFx( belugas@6655: (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@7928: if (HasBit(indsp->callback_flags, CBM_IND_PRODUCTION_256_TICKS)) IndustryProductionCallback(i, 1); rubidium@7168: glx@7757: IndustryBehaviour indbehav = indsp->behaviour; rubidium@7165: i->produced_cargo_waiting[0] = min(0xffff, i->produced_cargo_waiting[0] + i->production_rate[0]); rubidium@7165: i->produced_cargo_waiting[1] = min(0xffff, i->produced_cargo_waiting[1] + i->production_rate[1]); truelight@0: rubidium@7205: if ((indbehav & INDUSTRYBEH_PLANT_FIELDS) != 0) { rubidium@7205: bool plant; skidd13@7928: if (HasBit(indsp->callback_flags, CBM_IND_SPECIAL_EFFECT)) { rubidium@7205: plant = (GetIndustryCallback(CBID_INDUSTRY_SPECIAL_EFFECT, Random(), 0, i, i->type, i->xy) != 0); rubidium@7205: } else { skidd13@7967: plant = Chance16(1, 8); rubidium@7205: } rubidium@7205: rubidium@7205: if (plant) PlantRandomFarmField(i); rubidium@7205: } rubidium@7205: if ((indbehav & INDUSTRYBEH_CUT_TREES) != 0) { rubidium@7205: bool cut = ((i->counter & 0x1FF) == 0); skidd13@7928: if (HasBit(indsp->callback_flags, CBM_IND_SPECIAL_EFFECT)) { rubidium@7205: cut = (GetIndustryCallback(CBID_INDUSTRY_SPECIAL_EFFECT, 0, 1, i, i->type, i->xy) != 0); rubidium@7205: } rubidium@7205: rubidium@7205: if (cut) ChopLumberMillTrees(i); tron@2639: } rubidium@9028: rubidium@9028: TriggerIndustry(i, INDUSTRY_TRIGGER_INDUSTRY_TICK); rubidium@9028: StartStopIndustryTileAnimation(i, IAT_INDUSTRY_TICK); truelight@0: } truelight@0: } truelight@0: rubidium@6247: 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) { 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: { rubidium@9413: if (_settings_game.game_creation.landscape == LT_ARCTIC) { maedhros@6343: 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; rubidium@9413: if (DistanceFromEdge(TILE_ADDXY(tile, 1, 1)) < _settings_game.game_creation.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 && rubidium@9413: DistanceFromEdge(TILE_ADDXY(tile, 1, 1)) < _settings_game.game_creation.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: { rubidium@9413: if (_settings_game.game_creation.landscape == LT_ARCTIC) { maedhros@6343: 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: rubidium@10236: t = ClosestTownFromTile(tile, UINT_MAX); truelight@201: rubidium@9413: if (_settings_game.economy.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@7662: bool IsSlopeRefused(Slope current, Slope refused) rubidium@7662: { belugas@7733: if (IsSteepSlope(current)) return true; rubidium@7662: if (current != SLOPE_FLAT) { glx@8030: if (IsSteepSlope(refused)) return true; rubidium@7662: rubidium@7662: Slope t = ComplementSlope(current); rubidium@7662: glx@8030: if (refused & SLOPE_W && (t & SLOPE_NW)) return true; glx@8030: if (refused & SLOPE_S && (t & SLOPE_NE)) return true; glx@8030: if (refused & SLOPE_E && (t & SLOPE_SW)) return true; glx@8030: if (refused & SLOPE_N && (t & SLOPE_SE)) return true; rubidium@7662: } rubidium@7662: rubidium@7662: return false; rubidium@7662: } rubidium@7662: rubidium@7662: 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@7663: bool refused_slope = false; rubidium@7663: bool custom_shape = false; truelight@0: truelight@0: do { belugas@7467: IndustryGfx gfx = GetTranslatedIndustryTileID(it->gfx); tron@1977: TileIndex cur_tile = tile + ToTileIndexDiff(it->ti); tron@1977: truelight@0: if (!IsValidTile(cur_tile)) { belugas@7473: if (gfx == GFX_WATERTILE_SPECIALCHECK) continue; truelight@0: return false; truelight@0: } truelight@201: belugas@7473: 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@7758: if (!EnsureNoVehicleOnGround(cur_tile)) return false; rubidium@6893: if (MayHaveBridgeAbove(cur_tile) && IsBridgeAbove(cur_tile)) return false; rubidium@6893: belugas@7531: const IndustryTileSpec *its = GetIndustryTileSpec(gfx); belugas@7531: glx@7757: IndustryBehaviour ind_behav = GetIndustrySpec(type)->behaviour; truelight@0: glx@7756: /* Perform land/water check if not disabled */ peter1138@8471: if (!HasBit(its->slopes_refused, 5) && (IsWaterTile(cur_tile) == !(ind_behav & INDUSTRYBEH_BUILT_ONWATER))) return false; glx@7756: skidd13@7928: if (HasBit(its->callback_flags, CBM_INDT_SHAPE_CHECK)) { rubidium@7663: custom_shape = true; rubidium@7662: if (!PerformIndustryTileSlopeCheck(tile, cur_tile, its, type, gfx, itspec_index)) return false; truelight@0: } else { glx@7756: Slope tileh = GetTileSlope(cur_tile, NULL); glx@7756: refused_slope |= IsSlopeRefused(tileh, its->slopes_refused); rubidium@7212: } truelight@0: glx@8054: if (ind_behav & (INDUSTRYBEH_ONLY_INTOWN | INDUSTRYBEH_TOWN1200_MORE)) { rubidium@7212: if (!IsTileType(cur_tile, MP_HOUSE)) { rubidium@8049: _error_message = STR_030D_CAN_ONLY_BE_BUILT_IN_TOWNS; rubidium@7212: return false; rubidium@7212: } frosch@10362: frosch@10362: /* Clear the tiles as OWNER_TOWN to not affect town rating, and to not clear protected buildings */ frosch@10362: CompanyID old_company = _current_company; frosch@10362: _current_company = OWNER_TOWN; frosch@10362: bool not_clearable = CmdFailed(DoCommand(cur_tile, 0, 0, 0, CMD_LANDSCAPE_CLEAR)); frosch@10362: _current_company = old_company; frosch@10362: frosch@10362: if (not_clearable) return false; rubidium@7227: } else if ((ind_behav & INDUSTRYBEH_ONLY_NEARTOWN) == 0 || !IsTileType(cur_tile, MP_HOUSE)) { frosch@10362: /* Clear the tiles as OWNER_TOWN to not affect town rating, and to not clear protected buildings */ frosch@10362: CompanyID old_company = _current_company; frosch@10362: _current_company = OWNER_TOWN; frosch@10362: bool not_clearable = CmdFailed(DoCommand(cur_tile, 0, 0, DC_AUTO, CMD_LANDSCAPE_CLEAR)); frosch@10362: _current_company = old_company; frosch@10362: frosch@10362: if (not_clearable) return false; truelight@0: } truelight@0: } tron@909: } while ((++it)->ti.x != -0x80); truelight@0: rubidium@7663: if (custom_shape_check != NULL) *custom_shape_check = custom_shape; rubidium@7663: rubidium@7663: /* It is almost impossible to have a fully flat land in TG, so what we rubidium@7663: * do is that we check if we can make the land flat later on. See rubidium@7663: * CheckIfCanLevelIndustryPlatform(). */ rubidium@9413: return !refused_slope || (_settings_game.game_creation.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@6390: 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@6390: 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@7970: 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@7629: 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@10207: /* _current_company is OWNER_NONE for randomly generated industries and in editor, or the company who funded or prospected the industry. rubidium@7640: * Perform terraforming as OWNER_TOWN to disable autoslope. */ rubidium@10207: CompanyID old_company = _current_company; rubidium@10207: _current_company = OWNER_TOWN; rubidium@7640: 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@7640: if (!CheckCanTerraformSurroundingTiles(tile_walk, h, 0)) { rubidium@10207: _current_company = old_company; rubidium@7640: return false; rubidium@7640: } 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@7640: if (CmdFailed(DoCommand(tile_walk, SLOPE_N, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND))) { rubidium@10207: _current_company = old_company; rubidium@7640: return false; rubidium@7640: } 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@7480: 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@10207: _current_company = old_company; truelight@4300: return true; truelight@4300: } truelight@4300: truelight@4300: belugas@8234: static bool CheckIfFarEnoughFromIndustry(TileIndex tile, int type) truelight@0: { belugas@3669: const IndustrySpec *indspec = GetIndustrySpec(type); belugas@3662: const Industry *i; truelight@0: rubidium@9413: if (_settings_game.economy.same_industry_close && indspec->IsRawIndustry()) belugas@8234: /* Allow primary industries to be placed close to any other industry */ truelight@0: return true; truelight@201: truelight@830: FOR_ALL_INDUSTRIES(i) { belugas@8234: /* Within 14 tiles from another industry is considered close */ belugas@8234: bool in_low_distance = DistanceMax(tile, i->xy) <= 14; belugas@8234: belugas@6201: /* check if an industry that accepts the same goods is nearby */ belugas@8234: if (in_low_distance && glx@9074: !indspec->IsRawIndustry() && // not a primary industry? glx@7645: indspec->accepts_cargo[0] == i->accepts_cargo[0] && ( belugas@8234: /* at least one of those options must be true */ belugas@8234: _game_mode != GM_EDITOR || // editor must not be stopped rubidium@9413: !_settings_game.economy.same_industry_close || rubidium@9413: !_settings_game.economy.multiple_industry_per_town)) { tron@2639: _error_message = STR_INDUSTRY_TOO_CLOSE; tron@2639: return false; tron@2639: } truelight@0: belugas@8234: /* check if there are any conflicting industry types around */ belugas@8234: if ((i->type == indspec->conflicting[0] || belugas@8234: i->type == indspec->conflicting[1] || belugas@8234: i->type == indspec->conflicting[2]) && belugas@8234: 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@8516: /** Production level maximum, minimum and default values. belugas@8516: * It is not a value been really used in order to change, but rather an indicator belugas@8516: * of how the industry is behaving. */ belugas@8516: enum ProductionLevels { belugas@8516: PRODLEVEL_CLOSURE = 0x00, ///< signal set to actually close the industry belugas@8516: PRODLEVEL_MINIMUM = 0x04, ///< below this level, the industry is set to be closing belugas@8516: PRODLEVEL_DEFAULT = 0x10, ///< default level set when the industry is created belugas@8516: PRODLEVEL_MAXIMUM = 0x80, ///< the industry is running at full speed belugas@8516: }; belugas@8516: belugas@7445: 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@7646: uint j; truelight@0: truelight@0: i->xy = tile; truelight@0: i->width = i->height = 0; truelight@0: i->type = type; belugas@6824: IncIndustryTypeCount(type); truelight@0: glx@7645: i->produced_cargo[0] = indspec->produced_cargo[0]; glx@7645: i->produced_cargo[1] = indspec->produced_cargo[1]; glx@7645: i->accepts_cargo[0] = indspec->accepts_cargo[0]; glx@7645: i->accepts_cargo[1] = indspec->accepts_cargo[1]; glx@7645: 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@7947: /* don't use smooth economy for industries using production related callbacks */ rubidium@9413: if (_settings_game.economy.smooth_economy && glx@7947: !(HasBit(indspec->callback_flags, CBM_IND_PRODUCTION_256_TICKS) || HasBit(indspec->callback_flags, CBM_IND_PRODUCTION_CARGO_ARRIVAL)) && // production callbacks glx@7947: !(HasBit(indspec->callback_flags, CBM_IND_MONTHLYPROD_CHANGE) || HasBit(indspec->callback_flags, CBM_IND_PRODUCTION_CHANGE)) // production change callbacks glx@7947: ) { 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@7862: i->random_color = GB(r, 0, 4); rubidium@7862: i->counter = GB(r, 4, 12); rubidium@7862: i->random = GB(r, 16, 16); rubidium@7165: i->produced_cargo_waiting[0] = 0; rubidium@7165: i->produced_cargo_waiting[1] = 0; rubidium@7165: i->incoming_cargo_waiting[0] = 0; rubidium@7165: i->incoming_cargo_waiting[1] = 0; rubidium@7165: i->incoming_cargo_waiting[2] = 0; rubidium@6819: i->this_month_production[0] = 0; rubidium@6819: i->this_month_production[1] = 0; rubidium@6819: i->this_month_transported[0] = 0; rubidium@6819: i->this_month_transported[1] = 0; rubidium@6819: i->last_month_pct_transported[0] = 0; rubidium@6819: i->last_month_pct_transported[1] = 0; rubidium@6819: i->last_month_transported[0] = 0; rubidium@6819: i->last_month_transported[1] = 0; truelight@0: i->was_cargo_delivered = false; rubidium@4329: i->last_prod_year = _cur_year; rubidium@6819: i->last_month_production[0] = i->production_rate[0] * 8; rubidium@6819: i->last_month_production[1] = i->production_rate[1] * 8; rubidium@10207: i->founder = _current_company; glx@7646: skidd13@7928: if (HasBit(indspec->callback_flags, CBM_IND_DECIDE_COLOUR)) { glx@7650: uint16 res = GetIndustryCallback(CBID_INDUSTRY_DECIDE_COLOUR, 0, 0, i, type, INVALID_TILE); glx@7650: if (res != CALLBACK_FAILED) i->random_color = GB(res, 0, 4); glx@7650: } glx@7650: skidd13@7928: if (HasBit(indspec->callback_flags, CBM_IND_INPUT_CARGO_TYPES)) { glx@7646: for (j = 0; j < lengthof(i->accepts_cargo); j++) i->accepts_cargo[j] = CT_INVALID; glx@7646: for (j = 0; j < lengthof(i->accepts_cargo); j++) { glx@7646: uint16 res = GetIndustryCallback(CBID_INDUSTRY_INPUT_CARGO_TYPES, j, 0, i, type, INVALID_TILE); glx@7646: if (res == CALLBACK_FAILED || GB(res, 0, 8) == CT_INVALID) break; glx@7646: i->accepts_cargo[j] = GetCargoTranslation(GB(res, 0, 8), indspec->grf_prop.grffile); glx@7646: } glx@7646: } glx@7646: skidd13@7928: if (HasBit(indspec->callback_flags, CBM_IND_OUTPUT_CARGO_TYPES)) { glx@7646: for (j = 0; j < lengthof(i->produced_cargo); j++) i->produced_cargo[j] = CT_INVALID; glx@7646: for (j = 0; j < lengthof(i->produced_cargo); j++) { glx@7646: uint16 res = GetIndustryCallback(CBID_INDUSTRY_OUTPUT_CARGO_TYPES, j, 0, i, type, INVALID_TILE); glx@7646: if (res == CALLBACK_FAILED || GB(res, 0, 8) == CT_INVALID) break; glx@7646: i->produced_cargo[j] = GetCargoTranslation(GB(res, 0, 8), indspec->grf_prop.grffile); glx@7646: } glx@7646: } glx@7646: rubidium@7186: i->construction_date = _date; rubidium@7186: i->construction_type = (_game_mode == GM_EDITOR) ? ICT_SCENARIO_EDITOR : rubidium@7186: (_generating_world ? ICT_MAP_GENERATION : ICT_NORMAL_GAMEPLAY); truelight@0: belugas@7445: /* Adding 1 here makes it conform to specs of var44 of varaction2 for industries belugas@7445: * 0 = created prior of newindustries belugas@7445: * else, chosen layout + 1 */ belugas@7445: i->selected_layout = layout + 1; belugas@7445: rubidium@6819: if (!_generating_world) i->last_month_production[0] = i->last_month_production[1] = 0; truelight@201: belugas@8516: i->prod_level = PRODLEVEL_DEFAULT; truelight@0: frosch@10362: /* Clear the tiles as OWNER_TOWN, to not affect town rating, and to not clear protected buildings */ frosch@10362: CompanyID old_company = _current_company; frosch@10362: _current_company = OWNER_TOWN; truelight@0: do { tron@1977: TileIndex cur_tile = tile + ToTileIndexDiff(it->ti); truelight@0: belugas@7473: 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: frosch@9718: WaterClass wc = (IsWaterTile(cur_tile) ? GetWaterClass(cur_tile) : WATER_CLASS_INVALID); frosch@9718: tron@3491: DoCommand(cur_tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR); truelight@0: frosch@9718: MakeIndustry(cur_tile, i->index, it->gfx, Random(), wc); rubidium@7853: rubidium@5435: if (_generating_world) { rubidium@5435: SetIndustryConstructionCounter(cur_tile, 3); rubidium@5435: SetIndustryConstructionStage(cur_tile, 2); rubidium@7862: } frosch@8311: frosch@8311: /* it->gfx is stored in the map. But the translated ID cur_gfx is the interesting one */ frosch@8311: IndustryGfx cur_gfx = GetTranslatedIndustryTileID(it->gfx); frosch@8311: const IndustryTileSpec *its = GetIndustryTileSpec(cur_gfx); frosch@8311: if (its->animation_info != 0xFFFF) AddAnimatedTile(cur_tile); truelight@0: } tron@909: } while ((++it)->ti.x != -0x80); frosch@10362: _current_company = old_company; truelight@0: truelight@0: i->width++; truelight@0: i->height++; truelight@0: belugas@6390: if (GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_PLANT_ON_BUILT) { truelight@4328: for (j = 0; j != 50; j++) PlantRandomFarmField(i); truelight@0: } glx@9306: InvalidateWindowData(WC_INDUSTRY_DIRECTORY, 0, 0); truelight@0: } truelight@0: belugas@6201: /** Helper function for Build/Fund an industry belugas@6201: * @param tile tile where industry is built belugas@6201: * @param type of industry to build belugas@6201: * @param flags of operations to conduct belugas@6201: * @param indspec pointer to industry specifications rubidium@7207: * @param itspec_index the index of the itsepc to build/fund rubidium@8436: * @param seed random seed (possibly) used by industries belugas@6201: * @return the pointer of the newly created industry, or NULL if it failed belugas@6201: */ rubidium@8436: static Industry *CreateNewIndustryHelper(TileIndex tile, IndustryType type, uint32 flags, const IndustrySpec *indspec, uint itspec_index, uint32 seed) truelight@4300: { rubidium@7207: const IndustryTileTable *it = indspec->table[itspec_index]; rubidium@7212: bool custom_shape_check = false; rubidium@7212: rubidium@7662: if (!CheckIfIndustryTilesAreFree(tile, it, itspec_index, type, &custom_shape_check)) return NULL; rubidium@7212: skidd13@7928: if (HasBit(GetIndustrySpec(type)->callback_flags, CBM_IND_LOCATION)) { rubidium@8436: if (!CheckIfCallBackAllowsCreation(tile, type, itspec_index, seed)) return NULL; rubidium@7212: } else { rubidium@7212: if (!_check_new_industry_procs[indspec->check_proc](tile)) return NULL; rubidium@7207: } truelight@4300: rubidium@9413: if (!custom_shape_check && _settings_game.game_creation.land_generator == LG_TERRAGENESIS && _generating_world && !_ignore_restrictions && !CheckIfCanLevelIndustryPlatform(tile, 0, it, type)) return NULL; belugas@8234: if (!CheckIfFarEnoughFromIndustry(tile, type)) return NULL; truelight@4300: rubidium@7207: 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@9036: if (!Industry::CanAllocateItem()) return NULL; truelight@4300: truelight@4300: if (flags & DC_EXEC) { rubidium@9036: Industry *i = new Industry(tile); rubidium@7212: if (!custom_shape_check) CheckIfCanLevelIndustryPlatform(tile, DC_EXEC, it, type); belugas@7445: DoCreateNewIndustry(i, tile, type, it, itspec_index, t, OWNER_NONE); rubidium@9036: rubidium@9036: return i; truelight@4300: } truelight@4300: rubidium@9036: /* We need to return a non-NULL pointer to tell we have created an industry. rubidium@9036: * However, we haven't created a real one (no DC_EXEC), so return a fake one. */ rubidium@9036: return GetIndustry(0); truelight@4300: } truelight@4300: Darkvater@1786: /** Build/Fund an industry tron@3491: * @param tile tile where industry is built belugas@6201: * @param flags of operations to conduct rubidium@8436: * @param p1 various bitstuffed elements rubidium@8436: * - p1 = (bit 0 - 15) - industry type see build_industry.h and see industry.h rubidium@8436: * - p1 = (bit 16 - 31) - first layout to try rubidium@8436: * @param p2 seed to use for variable 8F belugas@6201: * @return index of the newly create industry, or CMD_ERROR if it failed Darkvater@1786: */ rubidium@6943: CommandCost CmdBuildIndustry(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) truelight@0: { rubidium@8436: const IndustrySpec *indspec = GetIndustrySpec(GB(p1, 0, 16)); belugas@9732: const Industry *ind = NULL; tron@2639: belugas@4965: /* Check if the to-be built/founded industry is available for this climate. */ belugas@6748: if (!indspec->enabled) { belugas@6748: return CMD_ERROR; belugas@6748: } truelight@0: tron@2638: /* If the patch for raw-material industries is not on, you cannot build raw-material industries. rubidium@7185: * Raw material industries are industries that do not accept cargo (at least for now) */ rubidium@9413: if (_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 0 && indspec->IsRawIndustry()) { tron@2638: return CMD_ERROR; tron@2638: } truelight@0: rubidium@9413: if (_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && indspec->IsRawIndustry()) { rubidium@7177: if (flags & DC_EXEC) { rubidium@7177: /* Prospecting has a chance to fail, however we cannot guarantee that something can rubidium@7177: * be built on the map, so the chance gets lower when the map is fuller, but there rubidium@7177: * is nothing we can really do about that. */ rubidium@7177: if (Random() <= indspec->prospecting_chance) { rubidium@7177: for (int i = 0; i < 5000; i++) { glx@8154: /* We should not have more than one Random() in a function call glx@8154: * because parameter evaluation order is not guaranteed in the c++ standard glx@8154: */ glx@8154: tile = RandomTile(); belugas@9732: ind = CreateNewIndustryHelper(tile, p1, flags, indspec, RandomRange(indspec->num_table), p2); maedhros@7203: if (ind != NULL) { maedhros@7203: break; maedhros@7203: } rubidium@7177: } rubidium@7177: } rubidium@7177: } rubidium@7177: } else { glx@7760: int count = indspec->num_table; glx@7760: const IndustryTileTable * const *itt = indspec->table; rubidium@8436: int num = Clamp(GB(p1, 16, 16), 0, count - 1); truelight@0: glx@7748: _error_message = STR_0239_SITE_UNSUITABLE; rubidium@7177: do { glx@7760: if (--count < 0) return CMD_ERROR; glx@7760: if (--num < 0) num = indspec->num_table - 1; rubidium@7662: } while (!CheckIfIndustryTilesAreFree(tile, itt[num], num, p1)); truelight@201: belugas@9732: ind = CreateNewIndustryHelper(tile, p1, flags, indspec, num, p2); belugas@9732: if (ind == NULL) return CMD_ERROR; belugas@9732: } belugas@9732: belugas@9732: if (flags & DC_EXEC && _game_mode != GM_EDITOR && ind != NULL) { belugas@9732: SetDParam(0, indspec->name); belugas@9732: if (indspec->new_industry_text > STR_LAST_STRINGID) { belugas@9732: SetDParam(1, STR_TOWN); belugas@9732: SetDParam(2, ind->town->index); belugas@9732: } else { belugas@9732: SetDParam(1, ind->town->index); belugas@9732: } belugas@9739: AddNewsItem(indspec->new_industry_text, NS_INDUSTRY_OPEN, ind->xy, 0); rubidium@7177: } rubidium@7177: rubidium@8230: 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@8436: uint32 seed = Random(); rubidium@8436: return CreateNewIndustryHelper(tile, type, DC_EXEC, indspec, RandomRange(indspec->num_table), seed); truelight@0: } truelight@0: belugas@7338: enum { belugas@7338: NB_NUMOFINDUSTRY = 11, belugas@7338: NB_DIFFICULTY_LEVEL = 5, belugas@7338: }; belugas@7338: belugas@7338: static const byte _numof_industry_table[NB_DIFFICULTY_LEVEL][NB_NUMOFINDUSTRY] = { belugas@6201: /* difficulty settings for number of industries */ belugas@3669: {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //none belugas@6560: {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@6442: /** This function is the one who really do the creation work belugas@6442: * of random industries during game creation belugas@6442: * @param type IndustryType of the desired industry belugas@6442: * @param amount of industries that need to be built */ belugas@3669: static void PlaceInitialIndustry(IndustryType type, int amount) truelight@0: { belugas@7338: /* We need to bypass the amount given in parameter if it exceeds the maximum dimension of the belugas@7338: * _numof_industry_table. newgrf can specify a big amount */ rubidium@9413: int num = (amount > NB_NUMOFINDUSTRY) ? amount : _numof_industry_table[_settings_game.difficulty.number_industries][amount]; belugas@6757: const IndustrySpec *ind_spc = GetIndustrySpec(type); ludde@2072: belugas@6442: /* These are always placed next to the coastline, so we scale by the perimeter instead. */ belugas@6757: num = (ind_spc->check_proc == CHECK_REFINERY || ind_spc->check_proc == CHECK_OIL_RIG) ? ScaleByMapSize1D(num) : ScaleByMapSize(num); truelight@110: rubidium@9413: if (_settings_game.difficulty.number_industries != 0) { rubidium@10207: CompanyID old_company = _current_company; rubidium@10207: _current_company = OWNER_NONE; terom@10439: 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: } terom@10439: } while (num && --num); darkvater@266: rubidium@10207: _current_company = old_company; orudge@61: } truelight@0: } truelight@0: belugas@6442: /** This function will create ramdon industries during game creation. belugas@6442: * It will scale the amount of industries by map size as well as difficulty level */ rubidium@6247: void GenerateIndustries() truelight@0: { truelight@4300: uint i = 0; belugas@6442: uint8 chance; belugas@6442: IndustryType it; belugas@6442: const IndustrySpec *ind_spc; truelight@4300: truelight@4300: /* Find the total amount of industries */ rubidium@9413: if (_settings_game.difficulty.number_industries > 0) { rubidium@7319: for (it = 0; it < NUM_INDUSTRYTYPES; it++) { truelight@4300: belugas@7261: ind_spc = GetIndustrySpec(it); belugas@7261: belugas@7261: if (!CheckIfCallBackAllowsAvailability(it, IACT_MAPGENERATION)) { belugas@7261: ResetIndustryCreationProbility(it); belugas@7261: } belugas@7261: rubidium@9413: chance = ind_spc->appear_creation[_settings_game.game_creation.landscape]; glx@7262: if (ind_spc->enabled && chance > 0) { belugas@6748: /* once the chance of appearance is determind, it have to be scaled by belugas@6748: * the difficulty level. The "chance" in question is more an index into belugas@6748: * the _numof_industry_table,in fact */ rubidium@9413: int num = (chance > NB_NUMOFINDUSTRY) ? chance : _numof_industry_table[_settings_game.difficulty.number_industries][chance]; belugas@6442: belugas@6748: /* These are always placed next to the coastline, so we scale by the perimeter instead. */ belugas@6757: num = (ind_spc->check_proc == CHECK_REFINERY || ind_spc->check_proc == CHECK_OIL_RIG) ? ScaleByMapSize1D(num) : ScaleByMapSize(num); belugas@6748: i += num; belugas@6748: } truelight@4300: } belugas@6442: } truelight@4300: truelight@4300: SetGeneratingWorldProgress(GWP_INDUSTRY, i); truelight@0: rubidium@9413: if (_settings_game.difficulty.number_industries > 0) { rubidium@7319: for (it = 0; it < NUM_INDUSTRYTYPES; it++) { belugas@7261: /* Once the number of industries has been determined, let's really create them. belugas@7261: * The test for chance allows us to try create industries that are available only belugas@7261: * for this landscape. belugas@7261: * @todo : Do we really have to pass chance as un-scaled value, since we've already belugas@7261: * processed that scaling above? No, don't think so. Will find a way. */ belugas@7261: ind_spc = GetIndustrySpec(it); belugas@7261: if (ind_spc->enabled) { rubidium@9413: chance = ind_spc->appear_creation[_settings_game.game_creation.landscape]; belugas@7261: if (chance > 0) PlaceInitialIndustry(it, chance); belugas@7261: } belugas@6748: } belugas@7261: } truelight@0: } truelight@0: truelight@0: static void UpdateIndustryStatistics(Industry *i) truelight@0: { truelight@0: byte pct; belugas@6467: bool refresh = false; truelight@201: glx@7645: for (byte j = 0; j < lengthof(i->produced_cargo); j++) { glx@7645: if (i->produced_cargo[j] != CT_INVALID) { belugas@6636: pct = 0; rubidium@6819: if (i->this_month_production[j] != 0) { belugas@6636: i->last_prod_year = _cur_year; rubidium@6819: pct = min(i->this_month_transported[j] * 256 / i->this_month_production[j], 255); belugas@6636: } rubidium@6819: i->last_month_pct_transported[j] = pct; truelight@0: rubidium@6819: i->last_month_production[j] = i->this_month_production[j]; rubidium@6819: i->this_month_production[j] = 0; truelight@0: rubidium@6819: i->last_month_transported[j] = i->this_month_transported[j]; rubidium@6819: i->this_month_transported[j] = 0; belugas@6636: refresh = true; belugas@6636: } truelight@0: } truelight@0: glx@7613: if (refresh) InvalidateWindow(WC_INDUSTRY_VIEW, i->index); truelight@0: } truelight@0: belugas@6431: /** Simple helper that will collect data for the generation of industries */ belugas@6431: struct ProbabilityHelper { belugas@6431: uint16 prob; ///< probability belugas@6431: IndustryType ind; ///< industry id correcponding belugas@6431: }; belugas@6431: belugas@6419: /** belugas@6419: * Try to create a random industry, during gameplay belugas@6419: */ belugas@6419: static void MaybeNewIndustry(void) belugas@6419: { belugas@6419: Industry *ind; //will receive the industry's creation pointer belugas@6419: IndustryType rndtype, j; // Loop controlers belugas@6419: const IndustrySpec *ind_spc; belugas@6419: uint num = 0; belugas@6493: ProbabilityHelper cumulative_probs[NUM_INDUSTRYTYPES]; // probability collector belugas@6419: uint16 probability_max = 0; truelight@0: belugas@6419: /* Generate a list of all possible industries that can be built. */ belugas@6493: for (j = 0; j < NUM_INDUSTRYTYPES; j++) { rubidium@7823: ind_spc = GetIndustrySpec(j); rubidium@9413: byte chance = ind_spc->appear_ingame[_settings_game.game_creation.landscape]; rubidium@7823: rubidium@7823: if (!ind_spc->enabled || chance == 0) continue; truelight@0: belugas@7261: /* If there is no Callback CBID_INDUSTRY_AVAILABLE or if this one did anot failed, belugas@7261: * and if appearing chance for this landscape is above 0, this industry can be chosen */ rubidium@7823: if (CheckIfCallBackAllowsAvailability(j, IACT_RANDOMCREATION)) { belugas@6419: probability_max += chance; belugas@6431: /* adds the result for this industry */ belugas@6431: cumulative_probs[num].ind = j; belugas@6431: cumulative_probs[num++].prob = probability_max; belugas@6419: } belugas@6419: } belugas@6419: belugas@6431: /* Find a random type, with maximum being what has been evaluate above*/ belugas@6419: rndtype = RandomRange(probability_max); belugas@6493: for (j = 0; j < NUM_INDUSTRYTYPES; j++) { belugas@6431: /* and choose the index of the industry that matches as close as possible this random type */ belugas@6431: if (cumulative_probs[j].prob >= rndtype) break; belugas@6419: } belugas@6419: belugas@6431: ind_spc = GetIndustrySpec(cumulative_probs[j].ind); belugas@6419: /* Check if it is allowed */ belugas@6390: if ((ind_spc->behaviour & INDUSTRYBEH_BEFORE_1950) && _cur_year > 1950) return; belugas@6390: if ((ind_spc->behaviour & INDUSTRYBEH_AFTER_1960) && _cur_year < 1960) return; truelight@0: belugas@6431: /* try to create 2000 times this industry */ belugas@6419: num = 2000; tron@2639: for (;;) { belugas@6431: ind = CreateNewIndustry(RandomTile(), cumulative_probs[j].ind); belugas@6419: if (ind != NULL) break; belugas@6419: if (--num == 0) return; truelight@0: } truelight@0: belugas@6266: SetDParam(0, ind_spc->name); glx@7755: if (ind_spc->new_industry_text > STR_LAST_STRINGID) { glx@7755: SetDParam(1, STR_TOWN); glx@7755: SetDParam(2, ind->town->index); glx@7755: } else { glx@7755: SetDParam(1, ind->town->index); glx@7755: } belugas@6266: AddNewsItem(ind_spc->new_industry_text, belugas@9739: NS_INDUSTRY_OPEN, ind->xy, 0); truelight@0: } truelight@0: glx@7613: /** glx@7613: * Protects an industry from closure if the appropriate flags and conditions are met glx@7613: * INDUSTRYBEH_CANCLOSE_LASTINSTANCE must be set (which, by default, it is not) and the glx@7613: * count of industries of this type must one (or lower) in order to be protected glx@7613: * against closure. glx@7613: * @param type IndustryType been queried glx@7613: * @result true if protection is on, false otherwise (except for oil wells) glx@7613: */ glx@7613: static bool CheckIndustryCloseDownProtection(IndustryType type) truelight@0: { glx@7613: const IndustrySpec *indspec = GetIndustrySpec(type); glx@7613: glx@7613: /* oil wells (or the industries with that flag set) are always allowed to closedown */ rubidium@9413: if (indspec->behaviour & INDUSTRYBEH_DONT_INCR_PROD && _settings_game.game_creation.landscape == LT_TEMPERATE) return false; rubidium@7821: return (indspec->behaviour & INDUSTRYBEH_CANCLOSE_LASTINSTANCE) == 0 && GetIndustryTypeCount(type) <= 1; glx@7613: } glx@7613: rubidium@7891: /** rubidium@7891: * Can given cargo type be accepted or produced by the industry? rubidium@7891: * @param cargo: Cargo type rubidium@7891: * @param ind: Industry rubidium@7891: * @param *c_accepts: Pointer to boolean for acceptance of cargo rubidium@7891: * @param *c_produces: Pointer to boolean for production of cargo rubidium@7891: * @return: \c *c_accepts is set when industry accepts the cargo type, rubidium@7891: * \c *c_produces is set when the industry produces the cargo type rubidium@7891: */ rubidium@7891: static void CanCargoServiceIndustry(CargoID cargo, Industry *ind, bool *c_accepts, bool *c_produces) rubidium@7891: { rubidium@7891: const IndustrySpec *indspec = GetIndustrySpec(ind->type); rubidium@7891: rubidium@7891: /* Check for acceptance of cargo */ glx@9074: for (byte j = 0; j < lengthof(ind->accepts_cargo); j++) { glx@9074: if (ind->accepts_cargo[j] == CT_INVALID) continue; rubidium@7891: if (cargo == ind->accepts_cargo[j]) { skidd13@7928: if (HasBit(indspec->callback_flags, CBM_IND_REFUSE_CARGO)) { rubidium@7891: uint16 res = GetIndustryCallback(CBID_INDUSTRY_REFUSE_CARGO, rubidium@7891: 0, GetReverseCargoTranslation(cargo, indspec->grf_prop.grffile), rubidium@7891: ind, ind->type, ind->xy); rubidium@7891: if (res == 0) continue; rubidium@7891: } rubidium@7891: *c_accepts = true; rubidium@7891: break; rubidium@7891: } rubidium@7891: } rubidium@7891: rubidium@7891: /* Check for produced cargo */ glx@9074: for (byte j = 0; j < lengthof(ind->produced_cargo); j++) { glx@9074: if (ind->produced_cargo[j] == CT_INVALID) continue; rubidium@7891: if (cargo == ind->produced_cargo[j]) { rubidium@7891: *c_produces = true; rubidium@7891: break; rubidium@7891: } rubidium@7891: } rubidium@7891: } rubidium@7891: rubidium@7891: /** rubidium@7891: * Compute who can service the industry. rubidium@7891: * rubidium@7891: * Here, 'can service' means that he/she has trains and stations close enough rubidium@7891: * to the industry with the right cargo type and the right orders (ie has the rubidium@7891: * technical means). rubidium@7891: * rubidium@7891: * @param ind: Industry being investigated. rubidium@7891: * rubidium@10207: * @return: 0 if nobody can service the industry, 2 if the local company can rubidium@7891: * service the industry, and 1 otherwise (only competitors can service the rubidium@7891: * industry) rubidium@7891: */ rubidium@7891: int WhoCanServiceIndustry(Industry* ind) rubidium@7891: { rubidium@7891: /* Find all stations within reach of the industry */ frosch@10353: StationSet stations = FindStationsAroundTiles(ind->xy, ind->width, ind->height); rubidium@7891: rubidium@7891: if (stations.size() == 0) return 0; // No stations found at all => nobody services rubidium@7891: rubidium@7891: const Vehicle *v; rubidium@7891: int result = 0; rubidium@7891: FOR_ALL_VEHICLES(v) { rubidium@7891: /* Is it worthwhile to try this vehicle? */ rubidium@10207: if (v->owner != _local_company && result != 0) continue; rubidium@7891: rubidium@7891: /* Check whether it accepts the right kind of cargo */ rubidium@7891: bool c_accepts = false; rubidium@7891: bool c_produces = false; rubidium@7891: if (v->type == VEH_TRAIN && IsFrontEngine(v)) { peter1138@9321: for (const Vehicle *u = v; u != NULL; u = u->Next()) { rubidium@7891: CanCargoServiceIndustry(u->cargo_type, ind, &c_accepts, &c_produces); peter1138@9321: } rubidium@7891: } else if (v->type == VEH_ROAD || v->type == VEH_SHIP || v->type == VEH_AIRCRAFT) { rubidium@7891: CanCargoServiceIndustry(v->cargo_type, ind, &c_accepts, &c_produces); rubidium@7891: } else { rubidium@7891: continue; rubidium@7891: } rubidium@7891: if (!c_accepts && !c_produces) continue; // Wrong cargo rubidium@7891: rubidium@7891: /* Check orders of the vehicle. rubidium@7891: * We cannot check the first of shared orders only, since the first vehicle in such a chain rubidium@7891: * may have a different cargo type. rubidium@7891: */ rubidium@7891: const Order *o; rubidium@7891: FOR_VEHICLE_ORDERS(v, o) { rubidium@8855: if (o->IsType(OT_GOTO_STATION) && !(o->GetUnloadType() & OUFB_TRANSFER)) { rubidium@7891: /* Vehicle visits a station to load or unload */ rubidium@8840: Station *st = GetStation(o->GetDestination()); rubidium@7891: if (!st->IsValid()) continue; rubidium@7891: rubidium@7891: /* Same cargo produced by industry is dropped here => not serviced by vehicle v */ rubidium@8855: if ((o->GetUnloadType() & OUFB_UNLOAD) && !c_accepts) break; rubidium@7891: rubidium@7891: if (stations.find(st) != stations.end()) { rubidium@10207: if (v->owner == _local_company) return 2; // Company services industry rubidium@7891: result = 1; // Competitor services industry rubidium@7891: } rubidium@7891: } rubidium@7891: } rubidium@7891: } rubidium@7891: return result; rubidium@7891: } rubidium@7891: rubidium@7891: /** rubidium@7891: * Report news that industry production has changed significantly rubidium@7891: * rubidium@7891: * @param ind: Industry with changed production rubidium@7891: * @param type: Cargo type that has changed rubidium@7891: * @param percent: Percentage of change (>0 means increase, <0 means decrease) rubidium@7891: */ rubidium@7891: static void ReportNewsProductionChangeIndustry(Industry *ind, CargoID type, int percent) rubidium@7891: { rubidium@9234: NewsSubtype ns; rubidium@7891: rubidium@7891: switch (WhoCanServiceIndustry(ind)) { rubidium@10207: case 0: ns = NS_INDUSTRY_NOBODY; break; rubidium@10207: case 1: ns = NS_INDUSTRY_OTHER; break; rubidium@10207: case 2: ns = NS_INDUSTRY_COMPANY; break; rubidium@7891: default: NOT_REACHED(); break; rubidium@7891: } rubidium@7891: SetDParam(2, abs(percent)); rubidium@7891: SetDParam(0, GetCargo(type)->name); rubidium@7891: SetDParam(1, ind->index); rubidium@7891: AddNewsItem( rubidium@7891: percent >= 0 ? STR_INDUSTRY_PROD_GOUP : STR_INDUSTRY_PROD_GODOWN, rubidium@9234: ns, rubidium@7891: ind->xy + TileDiffXY(1, 1), 0 rubidium@7891: ); rubidium@7891: } rubidium@7891: belugas@7945: enum { belugas@7945: PERCENT_TRANSPORTED_60 = 153, belugas@8203: PERCENT_TRANSPORTED_80 = 204, belugas@7945: }; belugas@7945: glx@7613: /** Change industry production or do closure glx@7613: * @param i Industry for which changes are performed glx@7613: * @param monthly true if it's the monthly call, false if it's the random call glx@7613: */ glx@7613: static void ChangeIndustryProduction(Industry *i, bool monthly) glx@7613: { truelight@1320: StringID str = STR_NULL; glx@7613: bool closeit = false; glx@7613: const IndustrySpec *indspec = GetIndustrySpec(i->type); glx@7615: bool standard = true; glx@7615: bool suppress_message = false; glx@7947: /* don't use smooth economy for industries using production related callbacks */ rubidium@9413: bool smooth_economy = _settings_game.economy.smooth_economy && glx@7947: !(HasBit(indspec->callback_flags, CBM_IND_PRODUCTION_256_TICKS) || HasBit(indspec->callback_flags, CBM_IND_PRODUCTION_CARGO_ARRIVAL)) && // production callbacks glx@7947: !(HasBit(indspec->callback_flags, CBM_IND_MONTHLYPROD_CHANGE) || HasBit(indspec->callback_flags, CBM_IND_PRODUCTION_CHANGE)); // production change callbacks glx@7615: byte div = 0; glx@7615: byte mul = 0; belugas@7976: int8 increment = 0; truelight@201: skidd13@7928: if (HasBit(indspec->callback_flags, monthly ? CBM_IND_MONTHLYPROD_CHANGE : CBM_IND_PRODUCTION_CHANGE)) { glx@7615: uint16 res = GetIndustryCallback(monthly ? CBID_INDUSTRY_MONTHLYPROD_CHANGE : CBID_INDUSTRY_PRODUCTION_CHANGE, 0, Random(), i, i->type, i->xy); glx@7947: standard = false; glx@7947: monthly = false; // smooth economy is disabled so we need to fake random industry production change to allow 'use standard' result glx@7615: if (res != CALLBACK_FAILED) { skidd13@7928: suppress_message = HasBit(res, 7); glx@7615: /* Get the custom message if any */ skidd13@7928: if (HasBit(res, 8)) str = MapGRFStringID(indspec->grf_prop.grffile->grfid, GB(GetRegister(0x100), 0, 16)); glx@7615: res = GB(res, 0, 4); glx@7615: switch(res) { glx@7615: default: NOT_REACHED(); glx@7615: case 0x0: break; // Do nothing, but show the custom message if any glx@7615: case 0x1: div = 1; break; // Halve industry production. If production reaches the quarter of the default, the industry is closed instead. glx@7615: case 0x2: mul = 1; break; // Double industry production if it hasn't reached eight times of the original yet. glx@7615: case 0x3: closeit = true; break; // The industry announces imminent closure, and is physically removed from the map next month. glx@7615: case 0x4: standard = true; break; // Do the standard random production change as if this industry was a primary one. glx@7615: case 0x5: case 0x6: case 0x7: // Divide production by 4, 8, 16 glx@7944: case 0x8: div = res - 0x3; break; // Divide production by 32 glx@7615: case 0x9: case 0xA: case 0xB: // Multiply production by 4, 8, 16 glx@7944: case 0xC: mul = res - 0x7; break; // Multiply production by 32 belugas@7976: case 0xD: // decrement production belugas@7976: case 0xE: // increment production belugas@7976: increment = res == 0x0D ? -1 : 1; belugas@7976: break; frosch@10317: case 0xF: // Set production to third byte of register 0x100 frosch@10317: i->prod_level = Clamp(GB(GetRegister(0x100), 16, 8), PRODLEVEL_MINIMUM, PRODLEVEL_MAXIMUM); frosch@10316: break; glx@7615: } glx@7615: } glx@7615: } truelight@1320: glx@7676: if (standard && monthly != smooth_economy) return; glx@7615: glx@7615: if (standard && indspec->life_type == INDUSTRYLIFE_BLACK_HOLE) return; glx@7615: glx@7615: if (standard && (indspec->life_type & (INDUSTRYLIFE_ORGANIC | INDUSTRYLIFE_EXTRACTIVE)) != 0) { rubidium@7144: /* decrease or increase */ rubidium@9413: bool only_decrease = (indspec->behaviour & INDUSTRYBEH_DONT_INCR_PROD) && _settings_game.game_creation.landscape == LT_TEMPERATE; rubidium@7144: glx@7676: if (smooth_economy) { glx@7613: closeit = true; glx@9074: for (byte j = 0; j < lengthof(i->produced_cargo); j++) { glx@9074: if (i->produced_cargo[j] == CT_INVALID) continue; belugas@8203: uint32 r = Random(); glx@7613: int old_prod, new_prod, percent; belugas@8203: /* If over 60% is transported, mult is 1, else mult is -1. */ belugas@7945: int mult = (i->last_month_pct_transported[j] > PERCENT_TRANSPORTED_60) ? 1 : -1; rubidium@7144: glx@7613: new_prod = old_prod = i->production_rate[j]; glx@7613: belugas@8203: /* For industries with only_decrease flags (temperate terrain Oil Wells), belugas@8203: * the multiplier will always be -1 so they will only decrease. */ maedhros@7972: if (only_decrease) { maedhros@7972: mult = -1; belugas@8203: /* For normal industries, if over 60% is transported, 33% chance for decrease. belugas@8203: * Bonus for very high station ratings (over 80%): 16% chance for decrease. */ belugas@8203: } else if (Chance16I(1, ((i->last_month_pct_transported[j] > PERCENT_TRANSPORTED_80) ? 6 : 3), r)) { maedhros@7972: mult *= -1; maedhros@7972: } belugas@7945: belugas@8203: /* 4.5% chance for 3-23% (or 1 unit for very low productions) production change, belugas@8203: * determined by mult value. If mult = 1 prod. increases, else (-1) it decreases. */ belugas@8203: if (Chance16I(1, 22, r >> 16)) { belugas@7945: new_prod += mult * (max(((RandomRange(50) + 10) * old_prod) >> 8, 1U)); rubidium@7144: } glx@7613: belugas@7945: /* Prevent production to overflow or Oil Rig passengers to be over-"produced" */ skidd13@7922: new_prod = Clamp(new_prod, 1, 255); belugas@7945: belugas@7945: if (((indspec->behaviour & INDUSTRYBEH_BUILT_ONWATER) != 0) && j == 1) belugas@7945: new_prod = Clamp(new_prod, 0, 16); belugas@7945: glx@7613: /* Do not stop closing the industry when it has the lowest possible production rate */ glx@7613: if (new_prod == old_prod && old_prod > 1) { glx@7613: closeit = false; glx@7613: continue; glx@7613: } glx@7613: glx@7613: percent = (old_prod == 0) ? 100 : (new_prod * 100 / old_prod - 100); glx@7613: i->production_rate[j] = new_prod; glx@7613: glx@7613: /* Close the industry when it has the lowest possible production rate */ glx@7613: if (new_prod > 1) closeit = false; glx@7613: rubidium@7891: if (abs(percent) >= 10) { rubidium@7891: ReportNewsProductionChangeIndustry(i, i->produced_cargo[j], percent); glx@7613: } glx@7613: } glx@7613: } else { skidd13@7967: if (only_decrease || Chance16(1, 3)) { belugas@8203: /* If more than 60% transported, 66% chance of increase, else 33% chance of increase */ skidd13@7967: if (!only_decrease && (i->last_month_pct_transported[0] > PERCENT_TRANSPORTED_60) != Chance16(1, 3)) { glx@7615: mul = 1; // Increase production truelight@1320: } else { glx@7615: div = 1; // Decrease production truelight@0: } truelight@0: } rubidium@7144: } rubidium@7144: } glx@7613: glx@7615: if (standard && indspec->life_type & INDUSTRYLIFE_PROCESSING) { skidd13@7967: if ( (byte)(_cur_year - i->last_prod_year) >= 5 && Chance16(1, smooth_economy ? 180 : 2)) { glx@7613: closeit = true; rubidium@7144: } truelight@1320: } truelight@1320: glx@7615: /* Increase if needed */ belugas@8516: while (mul-- != 0 && i->prod_level < PRODLEVEL_MAXIMUM) { belugas@8517: i->prod_level = min(i->prod_level * 2, PRODLEVEL_MAXIMUM); glx@7615: i->production_rate[0] = min(i->production_rate[0] * 2, 0xFF); glx@7615: i->production_rate[1] = min(i->production_rate[1] * 2, 0xFF); glx@7615: if (str == STR_NULL) str = indspec->production_up_text; glx@7615: } glx@7615: glx@7615: /* Decrease if needed */ glx@7615: while (div-- != 0 && !closeit) { belugas@8516: if (i->prod_level == PRODLEVEL_MINIMUM) { glx@7615: closeit = true; glx@7615: } else { belugas@8517: i->prod_level = max(i->prod_level / 2, (int)PRODLEVEL_MINIMUM); // typecast to int required to please MSVC belugas@8517: i->production_rate[0] = (i->production_rate[0] + 1) / 2; belugas@8517: i->production_rate[1] = (i->production_rate[1] + 1) / 2; glx@7615: if (str == STR_NULL) str = indspec->production_down_text; glx@7615: } glx@7615: } glx@7615: belugas@8406: /* Increase or Decreasing the production level if needed */ belugas@7976: if (increment != 0) { belugas@8516: if (increment < 0 && i->prod_level == PRODLEVEL_MINIMUM) { belugas@8406: closeit = true; belugas@8406: } else { belugas@8516: i->prod_level = ClampU(i->prod_level + increment, PRODLEVEL_MINIMUM, PRODLEVEL_MAXIMUM); belugas@8406: } belugas@7976: } belugas@7976: glx@7613: /* Close if needed and allowed */ glx@7613: if (closeit && !CheckIndustryCloseDownProtection(i->type)) { belugas@8516: i->prod_level = PRODLEVEL_CLOSURE; glx@7613: str = indspec->closure_text; glx@7613: } glx@7613: glx@7615: if (!suppress_message && str != STR_NULL) { rubidium@9234: NewsSubtype ns; rubidium@7891: /* Compute news category */ rubidium@7891: if (closeit) { belugas@9739: ns = NS_INDUSTRY_CLOSE; rubidium@7891: } else { rubidium@7891: switch (WhoCanServiceIndustry(i)) { rubidium@10207: case 0: ns = NS_INDUSTRY_NOBODY; break; rubidium@10207: case 1: ns = NS_INDUSTRY_OTHER; break; rubidium@10207: case 2: ns = NS_INDUSTRY_COMPANY; break; rubidium@7891: default: NOT_REACHED(); break; rubidium@7891: } rubidium@7891: } rubidium@7891: /* Set parameters of news string */ glx@7755: if (str > STR_LAST_STRINGID) { glx@7755: SetDParam(0, STR_TOWN); glx@7755: SetDParam(1, i->town->index); glx@7755: SetDParam(2, indspec->name); rubidium@7870: } else if (closeit) { rubidium@7870: SetDParam(0, STR_INDUSTRY_FORMAT); rubidium@7870: SetDParam(1, i->town->index); rubidium@7870: SetDParam(2, indspec->name); glx@7755: } else { glx@7755: SetDParam(0, i->index); glx@7755: } rubidium@7891: /* and report the news to the user */ glx@7613: AddNewsItem(str, rubidium@9234: ns, glx@7613: i->xy + TileDiffXY(1, 1), 0); truelight@0: } truelight@0: } truelight@0: belugas@10146: /** Daily handler for the industry changes belugas@10146: * Taking the original map size of 256*256, the number of random changes was always of just one unit. belugas@10146: * But it cannot be the same on smaller or bigger maps. That number has to be scaled up or down. belugas@10146: * For small maps, it implies that less than one change per month is required, while on bigger maps, belugas@10146: * it would be way more. The daily loop handles those changes. */ belugas@10146: void IndustryDailyLoop() belugas@10146: { belugas@10146: _economy.industry_daily_change_counter += _economy.industry_daily_increment; belugas@10146: belugas@10146: /* Bits 16-31 of industry_construction_counter contain the number of industries to change/create today, belugas@10146: * the lower 16 bit are a fractional part that might accumulate over several days until it belugas@10146: * is sufficient for an industry. */ belugas@10146: uint16 change_loop = _economy.industry_daily_change_counter >> 16; belugas@10146: belugas@10146: /* Reset the active part of the counter, just keeping the "factional part" */ belugas@10146: _economy.industry_daily_change_counter &= 0xFFFF; belugas@10146: belugas@10146: if (change_loop == 0) { belugas@10146: return; // Nothing to do? get out belugas@10146: } belugas@10146: rubidium@10207: CompanyID old_company = _current_company; rubidium@10207: _current_company = OWNER_NONE; belugas@10146: belugas@10146: /* perform the required industry changes for the day */ belugas@10146: for (uint16 j = 0; j < change_loop; j++) { belugas@10146: /* 3% chance that we start a new industry */ belugas@10146: if (Chance16(3, 100)) { belugas@10146: MaybeNewIndustry(); belugas@10146: } else { belugas@10146: Industry *i = GetRandomIndustry(); belugas@10146: if (i != NULL) ChangeIndustryProduction(i, false); belugas@10146: } belugas@10146: } belugas@10146: rubidium@10207: _current_company = old_company; belugas@10146: belugas@10146: /* production-change */ belugas@10146: InvalidateWindowData(WC_INDUSTRY_DIRECTORY, 0, 1); belugas@10146: } belugas@10146: rubidium@6247: void IndustryMonthlyLoop() truelight@0: { truelight@0: Industry *i; rubidium@10207: CompanyID old_company = _current_company; rubidium@10207: _current_company = OWNER_NONE; truelight@0: truelight@830: FOR_ALL_INDUSTRIES(i) { truelight@4346: UpdateIndustryStatistics(i); belugas@8516: if (i->prod_level == PRODLEVEL_CLOSURE) { glx@7613: delete i; glx@7613: } else { glx@7613: ChangeIndustryProduction(i, true); glx@7613: } truelight@0: } truelight@0: rubidium@10207: _current_company = old_company; darkvater@266: belugas@6201: /* production-change */ glx@9306: InvalidateWindowData(WC_INDUSTRY_DIRECTORY, 0, 1); truelight@0: } truelight@0: truelight@0: rubidium@6247: void InitializeIndustries() truelight@0: { rubidium@7401: _Industry_pool.CleanPool(); rubidium@7401: _Industry_pool.AddBlockToPool(); truelight@919: belugas@6839: ResetIndustryCounts(); rubidium@4587: _industry_sound_tile = 0; truelight@0: } truelight@0: rubidium@7185: bool IndustrySpec::IsRawIndustry() const rubidium@7185: { rubidium@7185: /* Lumber mills are extractive/organic, but can always be built like a non-raw industry */ rubidium@7185: return (this->life_type & (INDUSTRYLIFE_EXTRACTIVE | INDUSTRYLIFE_ORGANIC)) != 0 && rubidium@7185: (this->behaviour & INDUSTRYBEH_CUT_TREES) == 0; rubidium@7185: } rubidium@7185: rubidium@7185: Money IndustrySpec::GetConstructionCost() const rubidium@7185: { rubidium@7185: return (_price.build_industry * rubidium@9413: (_settings_game.construction.raw_industry_construction == 1 && this->IsRawIndustry() ? rubidium@7185: this->raw_industry_cost_multiplier : rubidium@7185: this->cost_multiplier rubidium@7185: )) >> 8; rubidium@7185: } rubidium@7185: belugas@7978: Money IndustrySpec::GetRemovalCost() const belugas@7978: { belugas@7978: return (_price.remove_house * this->removal_cost_multiplier) >> 8; belugas@7978: } belugas@7978: rubidium@7494: static CommandCost TerraformTile_Industry(TileIndex tile, uint32 flags, uint z_new, Slope tileh_new) rubidium@7494: { rubidium@7582: if (AutoslopeEnabled()) { rubidium@7582: /* We imitate here TTDP's behaviour: rubidium@7582: * - Both new and old slope must not be steep. rubidium@7582: * - TileMaxZ must not be changed. rubidium@7582: * - Allow autoslope by default. rubidium@7582: * - Disallow autoslope if callback succeeds and returns non-zero. rubidium@7582: */ rubidium@7582: Slope tileh_old = GetTileSlope(tile, NULL); rubidium@7582: /* TileMaxZ must not be changed. Slopes must not be steep. */ rubidium@7582: if (!IsSteepSlope(tileh_old) && !IsSteepSlope(tileh_new) && (GetTileMaxZ(tile) == z_new + GetSlopeMaxZ(tileh_new))) { rubidium@7582: const IndustryGfx gfx = GetIndustryGfx(tile); rubidium@7582: const IndustryTileSpec *itspec = GetIndustryTileSpec(gfx); rubidium@7582: rubidium@7582: /* Call callback 3C 'disable autosloping for industry tiles'. */ skidd13@7928: if (HasBit(itspec->callback_flags, CBM_INDT_AUTOSLOPE)) { rubidium@7582: /* If the callback fails, allow autoslope. */ rubidium@7582: uint16 res = GetIndustryTileCallback(CBID_INDUSTRY_AUTOSLOPE, 0, 0, gfx, GetIndustryByTile(tile), tile); rubidium@8230: if ((res == 0) || (res == CALLBACK_FAILED)) return CommandCost(EXPENSES_CONSTRUCTION, _price.terraform); rubidium@7582: } else { rubidium@7891: /* allow autoslope */ rubidium@8230: return CommandCost(EXPENSES_CONSTRUCTION, _price.terraform); rubidium@7582: } rubidium@7582: } rubidium@7582: } rubidium@7637: return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR); rubidium@7494: } rubidium@7185: rubidium@5587: 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@7335: GetFoundation_Industry, /* get_foundation_proc */ rubidium@7494: TerraformTile_Industry, /* terraform_tile_proc */ truelight@0: }; truelight@0: Darkvater@1881: static const SaveLoad _industry_desc[] = { rubidium@6819: SLE_CONDVAR(Industry, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), rubidium@6819: SLE_CONDVAR(Industry, xy, SLE_UINT32, 6, SL_MAX_VERSION), rubidium@6819: SLE_VAR(Industry, width, SLE_UINT8), rubidium@6819: SLE_VAR(Industry, height, SLE_UINT8), rubidium@6819: SLE_REF(Industry, town, REF_TOWN), rubidium@8359: SLE_CONDNULL( 2, 0, 60), ///< used to be industry's produced_cargo glx@7645: SLE_CONDARR(Industry, produced_cargo, SLE_UINT8, 2, 78, SL_MAX_VERSION), rubidium@7165: SLE_CONDARR(Industry, incoming_cargo_waiting, SLE_UINT16, 3, 70, SL_MAX_VERSION), rubidium@7165: SLE_ARR(Industry, produced_cargo_waiting, SLE_UINT16, 2), rubidium@6819: SLE_ARR(Industry, production_rate, SLE_UINT8, 2), rubidium@8359: SLE_CONDNULL( 3, 0, 60), ///< used to be industry's accepts_cargo glx@7645: SLE_CONDARR(Industry, accepts_cargo, SLE_UINT8, 3, 78, SL_MAX_VERSION), rubidium@6819: SLE_VAR(Industry, prod_level, SLE_UINT8), rubidium@6819: SLE_ARR(Industry, this_month_production, SLE_UINT16, 2), rubidium@6819: SLE_ARR(Industry, this_month_transported, SLE_UINT16, 2), rubidium@6819: SLE_ARR(Industry, last_month_pct_transported, SLE_UINT8, 2), rubidium@6819: SLE_ARR(Industry, last_month_production, SLE_UINT16, 2), rubidium@6819: SLE_ARR(Industry, last_month_transported, SLE_UINT16, 2), truelight@0: rubidium@6819: SLE_VAR(Industry, counter, SLE_UINT16), truelight@0: rubidium@6819: SLE_VAR(Industry, type, SLE_UINT8), rubidium@6819: SLE_VAR(Industry, owner, SLE_UINT8), rubidium@6819: SLE_VAR(Industry, random_color, SLE_UINT8), rubidium@6819: SLE_CONDVAR(Industry, last_prod_year, SLE_FILE_U8 | SLE_VAR_I32, 0, 30), rubidium@6819: SLE_CONDVAR(Industry, last_prod_year, SLE_INT32, 31, SL_MAX_VERSION), rubidium@6819: SLE_VAR(Industry, was_cargo_delivered, SLE_UINT8), truelight@0: rubidium@7522: SLE_CONDVAR(Industry, founder, SLE_UINT8, 70, SL_MAX_VERSION), rubidium@7186: SLE_CONDVAR(Industry, construction_date, SLE_INT32, 70, SL_MAX_VERSION), rubidium@7186: SLE_CONDVAR(Industry, construction_type, SLE_UINT8, 70, SL_MAX_VERSION), rubidium@7186: SLE_CONDVAR(Industry, last_cargo_accepted_at, SLE_INT32, 70, SL_MAX_VERSION), belugas@7445: SLE_CONDVAR(Industry, selected_layout, SLE_UINT8, 73, SL_MAX_VERSION), rubidium@7186: rubidium@7610: SLE_CONDARRX(cpp_offsetof(Industry, psa) + cpp_offsetof(Industry::PersistentStorage, storage), SLE_UINT32, 16, 76, SL_MAX_VERSION), rubidium@7610: rubidium@7860: SLE_CONDVAR(Industry, random_triggers, SLE_UINT8, 82, SL_MAX_VERSION), rubidium@7860: SLE_CONDVAR(Industry, random, SLE_UINT16, 82, SL_MAX_VERSION), rubidium@7860: belugas@6201: /* 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@6247: static void Save_INDY() truelight@0: { truelight@0: Industry *ind; truelight@919: belugas@7288: /* 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@7290: /* Save and load the mapping between the industry/tile id on the map, and the grf file belugas@7290: * it came from. */ belugas@7290: static const SaveLoad _industries_id_mapping_desc[] = { belugas@7290: SLE_VAR(EntityIDMapping, grfid, SLE_UINT32), belugas@7290: SLE_VAR(EntityIDMapping, entity_id, SLE_UINT8), belugas@7290: SLE_VAR(EntityIDMapping, substitute_id, SLE_UINT8), belugas@7290: SLE_END() belugas@7290: }; belugas@7290: belugas@7290: static void Save_IIDS() belugas@7290: { belugas@7290: uint i; belugas@7290: uint j = _industry_mngr.GetMaxMapping(); belugas@7290: belugas@7290: for (i = 0; i < j; i++) { belugas@7290: SlSetArrayIndex(i); belugas@7290: SlObject(&_industry_mngr.mapping_ID[i], _industries_id_mapping_desc); belugas@7290: } belugas@7290: } belugas@7290: belugas@7290: static void Save_TIDS() belugas@7290: { belugas@7290: uint i; belugas@7290: uint j = _industile_mngr.GetMaxMapping(); belugas@7290: belugas@7290: for (i = 0; i < j; i++) { belugas@7290: SlSetArrayIndex(i); belugas@7290: SlObject(&_industile_mngr.mapping_ID[i], _industries_id_mapping_desc); belugas@7290: } belugas@7290: } belugas@7290: rubidium@6247: static void Load_INDY() truelight@0: { truelight@0: int index; truelight@1267: belugas@6839: ResetIndustryCounts(); truelight@1267: truelight@0: while ((index = SlIterateArray()) != -1) { rubidium@7390: Industry *i = new (index) Industry(); truelight@919: SlObject(i, _industry_desc); belugas@6824: IncIndustryTypeCount(i->type); truelight@0: } truelight@0: } truelight@0: belugas@7290: static void Load_IIDS() belugas@7290: { belugas@7290: int index; belugas@7290: uint max_id; belugas@7290: belugas@7290: /* clear the current mapping stored. belugas@7290: * This will create the manager if ever it is not yet done */ belugas@7290: _industry_mngr.ResetMapping(); belugas@7290: belugas@7290: /* get boundary for the temporary map loader NUM_INDUSTRYTYPES? */ belugas@7290: max_id = _industry_mngr.GetMaxMapping(); belugas@7290: belugas@7290: while ((index = SlIterateArray()) != -1) { belugas@7290: if ((uint)index >= max_id) break; belugas@7290: SlObject(&_industry_mngr.mapping_ID[index], _industries_id_mapping_desc); belugas@7290: } belugas@7290: } belugas@7290: belugas@7290: static void Load_TIDS() belugas@7290: { belugas@7290: int index; belugas@7290: uint max_id; belugas@7290: belugas@7290: /* clear the current mapping stored. belugas@7290: * This will create the manager if ever it is not yet done */ belugas@7290: _industile_mngr.ResetMapping(); belugas@7290: belugas@7290: /* get boundary for the temporary map loader NUM_INDUSTILES? */ belugas@7292: max_id = _industile_mngr.GetMaxMapping(); belugas@7290: belugas@7290: while ((index = SlIterateArray()) != -1) { belugas@7290: if ((uint)index >= max_id) break; belugas@7290: SlObject(&_industile_mngr.mapping_ID[index], _industries_id_mapping_desc); belugas@7290: } belugas@7290: } belugas@7290: rubidium@5587: extern const ChunkHandler _industry_chunk_handlers[] = { belugas@7290: { 'INDY', Save_INDY, Load_INDY, CH_ARRAY}, belugas@7290: { 'IIDS', Save_IIDS, Load_IIDS, CH_ARRAY}, belugas@7290: { 'TIDS', Save_TIDS, Load_TIDS, CH_ARRAY | CH_LAST}, truelight@0: };