/* $Id$ */ #include "stdafx.h" #include "openttd.h" #include "clear_map.h" #include "industry_map.h" #include "station_map.h" #include "table/strings.h" #include "table/sprites.h" #include "functions.h" #include "map.h" #include "tile.h" #include "viewport.h" #include "command.h" #include "industry.h" #include "town.h" #include "vehicle.h" #include "news.h" #include "saveload.h" #include "economy.h" #include "sound.h" #include "variables.h" #include "table/industry_land.h" #include "table/build_industry.h" #include "genworld.h" #include "date.h" enum { /* Max industries: 64000 (8 * 8000) */ INDUSTRY_POOL_BLOCK_SIZE_BITS = 3, /* In bits, so (1 << 3) == 8 */ INDUSTRY_POOL_MAX_BLOCKS = 8000, }; /** * Called if a new block is added to the industry-pool */ static void IndustryPoolNewBlock(uint start_item) { Industry *i; FOR_ALL_INDUSTRIES_FROM(i, start_item) i->index = start_item++; } /* Initialize the industry-pool */ MemoryPool _industry_pool = { "Industry", INDUSTRY_POOL_MAX_BLOCKS, INDUSTRY_POOL_BLOCK_SIZE_BITS, sizeof(Industry), &IndustryPoolNewBlock, NULL, 0, 0, NULL }; static byte _industry_sound_ctr; static TileIndex _industry_sound_tile; void ShowIndustryViewWindow(int industry); void BuildOilRig(TileIndex tile); void DeleteOilRig(TileIndex tile); static const IndustryType _industry_close_mode[IT_END] = { /* COAL_MINE */ INDUSTRYLIFE_PRODUCTION, /* POWER_STATION */ INDUSTRYLIFE_NOT_CLOSABLE, /* SAWMILL */ INDUSTRYLIFE_CLOSABLE, /* FOREST */ INDUSTRYLIFE_PRODUCTION, /* OIL_REFINERY */ INDUSTRYLIFE_CLOSABLE, /* OIL_RIG */ INDUSTRYLIFE_PRODUCTION, /* FACTORY */ INDUSTRYLIFE_CLOSABLE, /* PRINTING_WORKS */ INDUSTRYLIFE_CLOSABLE, /* STEEL_MILL */ INDUSTRYLIFE_CLOSABLE, /* FARM */ INDUSTRYLIFE_PRODUCTION, /* COPPER_MINE */ INDUSTRYLIFE_PRODUCTION, /* OIL_WELL */ INDUSTRYLIFE_PRODUCTION, /* BANK */ INDUSTRYLIFE_NOT_CLOSABLE, /* FOOD_PROCESS */ INDUSTRYLIFE_CLOSABLE, /* PAPER_MILL */ INDUSTRYLIFE_CLOSABLE, /* GOLD_MINE */ INDUSTRYLIFE_PRODUCTION, /* BANK_2, */ INDUSTRYLIFE_NOT_CLOSABLE, /* DIAMOND_MINE */ INDUSTRYLIFE_PRODUCTION, /* IRON_MINE */ INDUSTRYLIFE_PRODUCTION, /* FRUIT_PLANTATION */ INDUSTRYLIFE_PRODUCTION, /* RUBBER_PLANTATION */ INDUSTRYLIFE_PRODUCTION, /* WATER_SUPPLY */ INDUSTRYLIFE_PRODUCTION, /* WATER_TOWER */ INDUSTRYLIFE_NOT_CLOSABLE, /* FACTORY_2 */ INDUSTRYLIFE_CLOSABLE, /* FARM_2 */ INDUSTRYLIFE_PRODUCTION, /* LUMBER_MILL */ INDUSTRYLIFE_CLOSABLE, /* COTTON_CANDY */ INDUSTRYLIFE_PRODUCTION, /* CANDY_FACTORY */ INDUSTRYLIFE_CLOSABLE, /* BATTERY_FARM */ INDUSTRYLIFE_PRODUCTION, /* COLA_WELLS */ INDUSTRYLIFE_PRODUCTION, /* TOY_SHOP */ INDUSTRYLIFE_NOT_CLOSABLE, /* TOY_FACTORY */ INDUSTRYLIFE_CLOSABLE, /* PLASTIC_FOUNTAINS */ INDUSTRYLIFE_PRODUCTION, /* FIZZY_DRINK_FACTORY */INDUSTRYLIFE_CLOSABLE, /* BUBBLE_GENERATOR */ INDUSTRYLIFE_PRODUCTION, /* TOFFEE_QUARRY */ INDUSTRYLIFE_PRODUCTION, /* SUGAR_MINE */ INDUSTRYLIFE_PRODUCTION }; /** * Retrieve the type for this industry. Although it is accessed by a tile, * it will return the general type of industry, and not the sprite index * as would do GetIndustryGfx. * The same information can be accessed by looking at Industry->type * @param tile that is queried * @pre IsTileType(tile, MP_INDUSTRY) * @return general type for this industry, as defined in industry.h **/ IndustryType GetIndustryType(TileIndex tile) { IndustryGfx this_type = GetIndustryGfx(tile); IndustryType iloop; assert(IsTileType(tile, MP_INDUSTRY)); for (iloop = IT_COAL_MINE; iloop < IT_END; iloop += 1) { if (IS_BYTE_INSIDE(this_type, industry_gfx_Solver[iloop].MinGfx, industry_gfx_Solver[iloop].MaxGfx+1)) { return iloop; } } return IT_INVALID; //we have not found equivalent, whatever the reason } /** * Accessor for array _industry_specs. * This will ensure at once : proper access and * not allowing modifications of it. * @param thistype of industry (which is the index in _industry_specs) * @pre thistype < IT_END **/ const IndustrySpec *GetIndustrySpec(IndustryType thistype) { assert(thistype < IT_END); return &_industry_specs[thistype]; } static void IndustryDrawSugarMine(const TileInfo *ti) { const DrawIndustrySpec1Struct *d; uint32 image; if (!IsIndustryCompleted(ti->tile)) return; d = &_draw_industry_spec1[_m[ti->tile].m3]; AddChildSpriteScreen(SPR_IT_SUGAR_MINE_SIEVE + d->image_1, d->x, 0); image = d->image_2; if (image != 0) AddChildSpriteScreen(SPR_IT_SUGAR_MINE_CLOUDS + image - 1, 8, 41); image = d->image_3; if (image != 0) { AddChildSpriteScreen(SPR_IT_SUGAR_MINE_PILE + image - 1, _drawtile_proc1_x[image - 1], _drawtile_proc1_y[image - 1]); } } static void IndustryDrawToffeeQuarry(const TileInfo *ti) { int x = 0; if (IsIndustryCompleted(ti->tile)) { x = _industry_anim_offs[_m[ti->tile].m3]; if ( (byte)x == 0xFF) x = 0; } AddChildSpriteScreen(SPR_IT_TOFFEE_QUARRY_SHOVEL, 22 - x, 24 + x); AddChildSpriteScreen(SPR_IT_TOFFEE_QUARRY_TOFFEE, 6, 14); } static void IndustryDrawBubbleGenerator( const TileInfo *ti) { if (IsIndustryCompleted(ti->tile)) { AddChildSpriteScreen(SPR_IT_BUBBLE_GENERATOR_BUBBLE, 5, _industry_anim_offs_2[_m[ti->tile].m3]); } else { AddChildSpriteScreen(SPR_IT_BUBBLE_GENERATOR_SPRING, 3, 67); } } static void IndustryDrawToyFactory(const TileInfo *ti) { const DrawIndustrySpec4Struct *d; d = &_industry_anim_offs_3[_m[ti->tile].m3]; if (d->image_1 != 0xFF) { AddChildSpriteScreen(SPR_IT_TOY_FACTORY_CLAY, 50 - d->image_1 * 2, 96 + d->image_1); } if (d->image_2 != 0xFF) { AddChildSpriteScreen(SPR_IT_TOY_FACTORY_ROBOT, 16 - d->image_2 * 2, 100 + d->image_2); } AddChildSpriteScreen(SPR_IT_TOY_FACTORY_STAMP, 7, d->image_3); AddChildSpriteScreen(SPR_IT_TOY_FACTORY_STAMP_HOLDER, 0, 42); } static void IndustryDrawCoalPlantSparks(const TileInfo *ti) { if (IsIndustryCompleted(ti->tile)) { uint image = GB(_m[ti->tile].m1, 2, 5); if (image != 0 && image < 7) { AddChildSpriteScreen(image + SPR_IT_POWER_PLANT_TRANSFORMERS, _coal_plant_sparks_x[image - 1], _coal_plant_sparks_y[image - 1] ); } } } typedef void IndustryDrawTileProc(const TileInfo *ti); static IndustryDrawTileProc * const _industry_draw_tile_procs[5] = { IndustryDrawSugarMine, IndustryDrawToffeeQuarry, IndustryDrawBubbleGenerator, IndustryDrawToyFactory, IndustryDrawCoalPlantSparks, }; static void DrawTile_Industry(TileInfo *ti) { const Industry *ind; const DrawBuildingsTileStruct *dits; byte z; uint32 image, ormod; /* Pointer to industry */ ind = GetIndustryByTile(ti->tile); ormod = GENERAL_SPRITE_COLOR(ind->color_map); /* Retrieve pointer to the draw industry tile struct */ dits = &_industry_draw_tile_data[GetIndustryGfx(ti->tile) << 2 | GetIndustryConstructionStage(ti->tile)]; image = dits->ground; if (image & PALETTE_MODIFIER_COLOR && (image & PALETTE_SPRITE_MASK) == 0) image |= ormod; z = ti->z; /* Add bricks below the industry? */ if (ti->tileh != SLOPE_FLAT) { AddSortableSpriteToDraw(SPR_FOUNDATION_BASE + ti->tileh, ti->x, ti->y, 16, 16, 7, z); AddChildSpriteScreen(image, 31, 1); z += TILE_HEIGHT; } else { /* Else draw regular ground */ DrawGroundSprite(image); } /* Add industry on top of the ground? */ image = dits->building; if (image != 0) { if (image & PALETTE_MODIFIER_COLOR && (image & PALETTE_SPRITE_MASK) == 0) image |= ormod; if (_display_opt & DO_TRANS_BUILDINGS) MAKE_TRANSPARENT(image); AddSortableSpriteToDraw(image, ti->x + dits->subtile_x, ti->y + dits->subtile_y, dits->width + 1, dits->height + 1, dits->dz, z); if (_display_opt & DO_TRANS_BUILDINGS) return; } { int proc = dits->draw_proc - 1; if (proc >= 0) _industry_draw_tile_procs[proc](ti); } } static uint GetSlopeZ_Industry(TileIndex tile, uint x, uint y) { return GetTileMaxZ(tile); } static Slope GetSlopeTileh_Industry(TileIndex tile, Slope tileh) { return SLOPE_FLAT; } static void GetAcceptedCargo_Industry(TileIndex tile, AcceptedCargo ac) { IndustryGfx gfx = GetIndustryGfx(tile); CargoID a; a = _industry_section_accepts_1[gfx]; if (a != CT_INVALID) ac[a] = (a == 0) ? 1 : 8; a = _industry_section_accepts_2[gfx]; if (a != CT_INVALID) ac[a] = 8; a = _industry_section_accepts_3[gfx]; if (a != CT_INVALID) ac[a] = 8; } static void GetTileDesc_Industry(TileIndex tile, TileDesc *td) { const Industry *i = GetIndustryByTile(tile); td->owner = i->owner; td->str = STR_4802_COAL_MINE + i->type; if (!IsIndustryCompleted(tile)) { SetDParamX(td->dparam, 0, td->str); td->str = STR_2058_UNDER_CONSTRUCTION; } } static int32 ClearTile_Industry(TileIndex tile, byte flags) { Industry *i = GetIndustryByTile(tile); /* * water can destroy industries * in editor you can bulldoze industries * with magic_bulldozer cheat you can destroy industries * (area around OILRIG is water, so water shouldn't flood it */ if ((_current_player != OWNER_WATER && _game_mode != GM_EDITOR && !_cheats.magic_bulldozer.value) || (_current_player == OWNER_WATER && i->type == IT_OIL_RIG)) { SetDParam(0, STR_4802_COAL_MINE + i->type); return_cmd_error(STR_4800_IN_THE_WAY); } if (flags & DC_EXEC) DeleteIndustry(i); return 0; } static void TransportIndustryGoods(TileIndex tile) { Industry *i = GetIndustryByTile(tile); const IndustrySpec *indspec = GetIndustrySpec(i->type); uint cw, am; cw = min(i->cargo_waiting[0], 255); if (cw > indspec->minimal_cargo/* && i->produced_cargo[0] != 0xFF*/) { i->cargo_waiting[0] -= cw; /* fluctuating economy? */ if (_economy.fluct <= 0) cw = (cw + 1) / 2; i->last_mo_production[0] += cw; am = MoveGoodsToStation(i->xy, i->width, i->height, i->produced_cargo[0], cw); i->last_mo_transported[0] += am; if (am != 0) { uint newgfx = _industry_produce_section[GetIndustryGfx(tile)]; if (newgfx != 0xFF) { ResetIndustryConstructionStage(tile); SetIndustryCompleted(tile, true); SetIndustryGfx(tile, newgfx); MarkTileDirtyByTile(tile); } } } cw = min(i->cargo_waiting[1], 255); if (cw > indspec->minimal_cargo) { i->cargo_waiting[1] -= cw; if (_economy.fluct <= 0) cw = (cw + 1) / 2; i->last_mo_production[1] += cw; am = MoveGoodsToStation(i->xy, i->width, i->height, i->produced_cargo[1], cw); i->last_mo_transported[1] += am; } } static void AnimateTile_Industry(TileIndex tile) { byte m; switch (GetIndustryGfx(tile)) { case GFX_SUGAR_MINE_SIEVE: if ((_tick_counter & 1) == 0) { m = _m[tile].m3 + 1; switch (m & 7) { case 2: SndPlayTileFx(SND_2D_RIP_2, tile); break; case 6: SndPlayTileFx(SND_29_RIP, tile); break; } if (m >= 96) { m = 0; DeleteAnimatedTile(tile); } _m[tile].m3 = m; MarkTileDirtyByTile(tile); } break; case GFX_TOFFEE_QUARY: if ((_tick_counter & 3) == 0) { m = _m[tile].m3; if (_industry_anim_offs[m] == 0xFF) { SndPlayTileFx(SND_30_CARTOON_SOUND, tile); } if (++m >= 70) { m = 0; DeleteAnimatedTile(tile); } _m[tile].m3 = m; MarkTileDirtyByTile(tile); } break; case GFX_BUBBLE_CATCHER: if ((_tick_counter&1) == 0) { m = _m[tile].m3; if (++m >= 40) { m = 0; DeleteAnimatedTile(tile); } _m[tile].m3 = m; MarkTileDirtyByTile(tile); } break; // Sparks on a coal plant case GFX_POWERPLANT_SPARKS: if ((_tick_counter & 3) == 0) { m = _m[tile].m1; if (GB(m, 2, 5) == 6) { SB(_m[tile].m1, 2, 5, 0); DeleteAnimatedTile(tile); } else { _m[tile].m1 = m + (1<<2); MarkTileDirtyByTile(tile); } } break; case GFX_TOY_FACTORY: if ((_tick_counter & 1) == 0) { m = _m[tile].m3 + 1; if (m == 1) { SndPlayTileFx(SND_2C_MACHINERY, tile); } else if (m == 23) { SndPlayTileFx(SND_2B_COMEDY_HIT, tile); } else if (m == 28) { SndPlayTileFx(SND_2A_EXTRACT_AND_POP, tile); } if (m >= 50) { int n = GetIndustryAnimationLoop(tile) + 1; m = 0; if (n >= 8) { n = 0; DeleteAnimatedTile(tile); } SetIndustryAnimationLoop(tile, n); } _m[tile].m3 = m; MarkTileDirtyByTile(tile); } break; case 148: case 149: case 150: case 151: case 152: case 153: case 154: case 155: if ((_tick_counter & 3) == 0) { IndustryGfx gfx = GetIndustryGfx(tile); gfx = (gfx < 155) ? gfx + 1 : 148; SetIndustryGfx(tile, gfx); MarkTileDirtyByTile(tile); } break; case GFX_OILWELL_ANIM1: case GFX_OILWELL_ANIM2: case GFX_OILWELL_ANIM3: if ((_tick_counter & 7) == 0) { bool b = CHANCE16(1,7); IndustryGfx gfx = GetIndustryGfx(tile); m = GB(_m[tile].m1, 0, 2) + 1; if (m == 4 && (m = 0, ++gfx) == GFX_OILWELL_ANIM3 + 1 && (gfx = GFX_OILWELL_ANIM1, b)) { _m[tile].m1 = 0x83; SetIndustryGfx(tile, GFX_OILWELL_BASE); DeleteAnimatedTile(tile); } else { SB(_m[tile].m1, 0, 2, m); SetIndustryGfx(tile, gfx); MarkTileDirtyByTile(tile); } } break; case 88: case 48: case 1: { int state = _tick_counter & 0x7FF; if ((state -= 0x400) < 0) return; if (state < 0x1A0) { if (state < 0x20 || state >= 0x180) { if (!(_m[tile].m1 & 0x40)) { _m[tile].m1 |= 0x40; SndPlayTileFx(SND_0B_MINING_MACHINERY, tile); } if (state & 7) return; } else { if (state & 3) return; } m = (_m[tile].m1 + 1) | 0x40; if (m > 0xC2) m = 0xC0; _m[tile].m1 = m; MarkTileDirtyByTile(tile); } else if (state >= 0x200 && state < 0x3A0) { int i; i = (state < 0x220 || state >= 0x380) ? 7 : 3; if (state & i) return; m = (_m[tile].m1 & 0xBF) - 1; if (m < 0x80) m = 0x82; _m[tile].m1 = m; MarkTileDirtyByTile(tile); } } break; } } static void CreateIndustryEffectSmoke(TileIndex tile) { uint x = TileX(tile) * TILE_SIZE; uint y = TileY(tile) * TILE_SIZE; uint z = GetTileMaxZ(tile); CreateEffectVehicle(x + 15, y + 14, z + 59, EV_CHIMNEY_SMOKE); } static void MakeIndustryTileBigger(TileIndex tile) { byte cnt = GetIndustryConstructionCounter(tile) + 1; byte stage; if (cnt != 4) { SetIndustryConstructionCounter(tile, cnt); return; } stage = GetIndustryConstructionStage(tile) + 1; SetIndustryConstructionCounter(tile, 0); SetIndustryConstructionStage(tile, stage); if (stage == 3) { SetIndustryCompleted(tile, true); } MarkTileDirtyByTile(tile); if (!IsIndustryCompleted(tile)) return; switch (GetIndustryGfx(tile)) { case 8: CreateIndustryEffectSmoke(tile); break; case 24: if (GetIndustryGfx(tile + TileDiffXY(0, 1)) == 24) BuildOilRig(tile); break; case GFX_TOY_FACTORY: case GFX_BUBBLE_CATCHER: case GFX_TOFFEE_QUARY: _m[tile].m3 = 0; SetIndustryAnimationLoop(tile, 0); break; case 148: case 149: case 150: case 151: case 152: case 153: case 154: case 155: AddAnimatedTile(tile); break; } } static void TileLoopIndustry_BubbleGenerator(TileIndex tile) { int dir; Vehicle *v; static const int8 _tileloop_ind_case_161[12] = { 11, 0, -4, -14, -4, -10, -4, 1, 49, 59, 60, 65, }; SndPlayTileFx(SND_2E_EXTRACT_AND_POP, tile); dir = Random() & 3; v = CreateEffectVehicleAbove( TileX(tile) * TILE_SIZE + _tileloop_ind_case_161[dir + 0], TileY(tile) * TILE_SIZE + _tileloop_ind_case_161[dir + 4], _tileloop_ind_case_161[dir + 8], EV_BUBBLE ); if (v != NULL) v->u.special.unk2 = dir; } static void TileLoop_Industry(TileIndex tile) { IndustryGfx newgfx; if (!IsIndustryCompleted(tile)) { MakeIndustryTileBigger(tile); return; } if (_game_mode == GM_EDITOR) return; TransportIndustryGoods(tile); newgfx = _industry_section_animation_next[GetIndustryGfx(tile)]; if (newgfx != 255) { ResetIndustryConstructionStage(tile); SetIndustryGfx(tile, newgfx); MarkTileDirtyByTile(tile); return; } #define SET_AND_ANIMATE(tile, a, b) { SetIndustryGfx(tile, a); _m[tile].m1 = b; AddAnimatedTile(tile); } #define SET_AND_UNANIMATE(tile, a, b) { SetIndustryGfx(tile, a); _m[tile].m1 = b; DeleteAnimatedTile(tile); } switch (GetIndustryGfx(tile)) { case 0x18: // coast line at oilrigs case 0x19: case 0x1A: case 0x1B: case 0x1C: TileLoop_Water(tile); break; case 0: if (!(_tick_counter & 0x400) && CHANCE16(1,2)) SET_AND_ANIMATE(tile,1,0x80); break; case 47: if (!(_tick_counter & 0x400) && CHANCE16(1,2)) SET_AND_ANIMATE(tile,0x30,0x80); break; case 79: if (!(_tick_counter & 0x400) && CHANCE16(1,2)) SET_AND_ANIMATE(tile,0x58,0x80); break; case 29: if (CHANCE16(1,6)) SET_AND_ANIMATE(tile,0x1E,0x80); break; case 1: if (!(_tick_counter & 0x400)) SET_AND_UNANIMATE(tile, 0, 0x83); break; case 48: if (!(_tick_counter & 0x400)) SET_AND_UNANIMATE(tile, 0x2F, 0x83); break; case 88: if (!(_tick_counter & 0x400)) SET_AND_UNANIMATE(tile, 0x4F, 0x83); break; case 10: if (CHANCE16(1,3)) { SndPlayTileFx(SND_0C_ELECTRIC_SPARK, tile); AddAnimatedTile(tile); } break; case 49: CreateEffectVehicleAbove(TileX(tile) * TILE_SIZE + 6, TileY(tile) * TILE_SIZE + 6, 43, EV_SMOKE); break; case 143: { Industry *i = GetIndustryByTile(tile); if (i->was_cargo_delivered) { i->was_cargo_delivered = false; SetIndustryAnimationLoop(tile, 0); AddAnimatedTile(tile); } } break; case 161: TileLoopIndustry_BubbleGenerator(tile); break; case 165: AddAnimatedTile(tile); break; case 174: if (CHANCE16(1, 3)) AddAnimatedTile(tile); break; } } static void ClickTile_Industry(TileIndex tile) { ShowIndustryViewWindow(GetIndustryIndex(tile)); } static uint32 GetTileTrackStatus_Industry(TileIndex tile, TransportType mode) { return 0; } static void GetProducedCargo_Industry(TileIndex tile, CargoID *b) { const Industry *i = GetIndustryByTile(tile); b[0] = i->produced_cargo[0]; b[1] = i->produced_cargo[1]; } static void ChangeTileOwner_Industry(TileIndex tile, PlayerID old_player, PlayerID new_player) { /* not used */ } void DeleteIndustry(Industry *i) { BEGIN_TILE_LOOP(tile_cur, i->width, i->height, i->xy); if (IsTileType(tile_cur, MP_INDUSTRY)) { if (GetIndustryIndex(tile_cur) == i->index) { DoClearSquare(tile_cur); } } else if (IsTileType(tile_cur, MP_STATION) && IsOilRig(tile_cur)) { DeleteOilRig(tile_cur); } END_TILE_LOOP(tile_cur, i->width, i->height, i->xy); i->xy = 0; _industry_sort_dirty = true; DeleteSubsidyWithIndustry(i->index); DeleteWindowById(WC_INDUSTRY_VIEW, i->index); InvalidateWindow(WC_INDUSTRY_DIRECTORY, 0); } static const byte _plantfarmfield_type[] = {1, 1, 1, 1, 1, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6}; static bool IsBadFarmFieldTile(TileIndex tile) { switch (GetTileType(tile)) { case MP_CLEAR: return IsClearGround(tile, CLEAR_FIELDS) || IsClearGround(tile, CLEAR_SNOW); case MP_TREES: return false; default: return true; } } static bool IsBadFarmFieldTile2(TileIndex tile) { switch (GetTileType(tile)) { case MP_CLEAR: return IsClearGround(tile, CLEAR_SNOW); case MP_TREES: return false; default: return true; } } static void SetupFarmFieldFence(TileIndex tile, int size, byte type, Axis direction) { do { tile = TILE_MASK(tile); if (IsTileType(tile, MP_CLEAR) || IsTileType(tile, MP_TREES)) { byte or = type; if (or == 1 && CHANCE16(1, 7)) or = 2; if (direction == AXIS_X) { SetFenceSE(tile, or); } else { SetFenceSW(tile, or); } } tile += (direction == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1)); } while (--size); } static void PlantFarmField(TileIndex tile) { uint size_x, size_y; uint32 r; uint count; uint counter; uint field_type; int type; if (_opt.landscape == LT_HILLY) { if (GetTileZ(tile) + TILE_HEIGHT * 2 >= _opt.snow_line) return; } /* determine field size */ r = (Random() & 0x303) + 0x404; if (_opt.landscape == LT_HILLY) r += 0x404; size_x = GB(r, 0, 8); size_y = GB(r, 8, 8); /* offset tile to match size */ tile -= TileDiffXY(size_x / 2, size_y / 2); /* check the amount of bad tiles */ count = 0; BEGIN_TILE_LOOP(cur_tile, size_x, size_y, tile) cur_tile = TILE_MASK(cur_tile); count += IsBadFarmFieldTile(cur_tile); END_TILE_LOOP(cur_tile, size_x, size_y, tile) if (count * 2 >= size_x * size_y) return; /* determine type of field */ r = Random(); counter = GB(r, 5, 3); field_type = GB(r, 8, 8) * 9 >> 8; /* make field */ BEGIN_TILE_LOOP(cur_tile, size_x, size_y, tile) cur_tile = TILE_MASK(cur_tile); if (!IsBadFarmFieldTile2(cur_tile)) { MakeField(cur_tile, field_type); SetClearCounter(cur_tile, counter); MarkTileDirtyByTile(cur_tile); } END_TILE_LOOP(cur_tile, size_x, size_y, tile) type = 3; if (_opt.landscape != LT_HILLY && _opt.landscape != LT_DESERT) { type = _plantfarmfield_type[Random() & 0xF]; } SetupFarmFieldFence(tile - TileDiffXY(1, 0), size_y, type, AXIS_Y); SetupFarmFieldFence(tile - TileDiffXY(0, 1), size_x, type, AXIS_X); SetupFarmFieldFence(tile + TileDiffXY(size_x - 1, 0), size_y, type, AXIS_Y); SetupFarmFieldFence(tile + TileDiffXY(0, size_y - 1), size_x, type, AXIS_X); } static void MaybePlantFarmField(const Industry *i) { if (CHANCE16(1, 8)) { int x = i->width / 2 + Random() % 31 - 16; int y = i->height / 2 + Random() % 31 - 16; TileIndex tile = TileAddWrap(i->xy, x, y); if (tile != INVALID_TILE) PlantFarmField(tile); } } static void ChopLumberMillTrees(Industry *i) { static const TileIndexDiffC _chop_dir[] = { { 0, 1}, { 1, 0}, { 0, -1}, {-1, 0} }; TileIndex tile = i->xy; int a; if (!IsIndustryCompleted(tile)) return; /* search outwards as a rectangular spiral */ for (a = 1; a != 41; a += 2) { uint dir; for (dir = 0; dir != 4; dir++) { int j = a; do { tile = TILE_MASK(tile); if (IsTileType(tile, MP_TREES)) { PlayerID old_player = _current_player; /* found a tree */ _current_player = OWNER_NONE; _industry_sound_ctr = 1; _industry_sound_tile = tile; SndPlayTileFx(SND_38_CHAINSAW, tile); DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR); SetTropicZone(tile, TROPICZONE_INVALID); i->cargo_waiting[0] = min(0xffff, i->cargo_waiting[0] + 45); _current_player = old_player; return; } tile += ToTileIndexDiff(_chop_dir[dir]); } while (--j); } tile -= TileDiffXY(1, 1); } } static const byte _industry_sounds[37][2] = { {0}, {0}, {1, SND_28_SAWMILL}, {0}, {0}, {0}, {1, SND_03_FACTORY_WHISTLE}, {1, SND_03_FACTORY_WHISTLE}, {0}, {3, SND_24_SHEEP}, {0}, {0}, {0}, {0}, {1, SND_28_SAWMILL}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {1, SND_03_FACTORY_WHISTLE}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {0}, {1, SND_33_PLASTIC_MINE}, {0}, {0}, {0}, {0}, }; static void ProduceIndustryGoods(Industry *i) { uint32 r; uint num; /* play a sound? */ if ((i->counter & 0x3F) == 0) { if (CHANCE16R(1,14,r) && (num=_industry_sounds[i->type][0]) != 0) { SndPlayTileFx( _industry_sounds[i->type][1] + (((r >> 16) * num) >> 16), i->xy); } } i->counter--; /* produce some cargo */ if ((i->counter & 0xFF) == 0) { i->cargo_waiting[0] = min(0xffff, i->cargo_waiting[0] + i->production_rate[0]); i->cargo_waiting[1] = min(0xffff, i->cargo_waiting[1] + i->production_rate[1]); if (i->type == IT_FARM) { MaybePlantFarmField(i); } else if (i->type == IT_LUMBER_MILL && (i->counter & 0x1FF) == 0) { ChopLumberMillTrees(i); } } } void OnTick_Industry(void) { Industry *i; if (_industry_sound_ctr != 0) { _industry_sound_ctr++; if (_industry_sound_ctr == 75) { SndPlayTileFx(SND_37_BALLOON_SQUEAK, _industry_sound_tile); } else if (_industry_sound_ctr == 160) { _industry_sound_ctr = 0; SndPlayTileFx(SND_36_CARTOON_CRASH, _industry_sound_tile); } } if (_game_mode == GM_EDITOR) return; FOR_ALL_INDUSTRIES(i) { if (i->xy != 0) ProduceIndustryGoods(i); } } static bool CheckNewIndustry_NULL(TileIndex tile) { return true; } static bool CheckNewIndustry_Forest(TileIndex tile) { if (_opt.landscape == LT_HILLY) { if (GetTileZ(tile) < _opt.snow_line + TILE_HEIGHT * 2U) { _error_message = STR_4831_FOREST_CAN_ONLY_BE_PLANTED; return false; } } return true; } static bool CheckNewIndustry_OilRefinery(TileIndex tile) { if (_game_mode == GM_EDITOR) return true; if (DistanceFromEdge(TILE_ADDXY(tile, 1, 1)) < _patches.oil_refinery_limit) return true; _error_message = STR_483B_CAN_ONLY_BE_POSITIONED; return false; } extern bool _ignore_restrictions; static bool CheckNewIndustry_OilRig(TileIndex tile) { if (_game_mode == GM_EDITOR && _ignore_restrictions) return true; if (TileHeight(tile) == 0 && DistanceFromEdge(TILE_ADDXY(tile, 1, 1)) < _patches.oil_refinery_limit) return true; _error_message = STR_483B_CAN_ONLY_BE_POSITIONED; return false; } static bool CheckNewIndustry_Farm(TileIndex tile) { if (_opt.landscape == LT_HILLY) { if (GetTileZ(tile) + TILE_HEIGHT * 2 >= _opt.snow_line) { _error_message = STR_0239_SITE_UNSUITABLE; return false; } } return true; } static bool CheckNewIndustry_Plantation(TileIndex tile) { if (GetTropicZone(tile) == TROPICZONE_DESERT) { _error_message = STR_0239_SITE_UNSUITABLE; return false; } return true; } static bool CheckNewIndustry_Water(TileIndex tile) { if (GetTropicZone(tile) != TROPICZONE_DESERT) { _error_message = STR_0318_CAN_ONLY_BE_BUILT_IN_DESERT; return false; } return true; } static bool CheckNewIndustry_Lumbermill(TileIndex tile) { if (GetTropicZone(tile) != TROPICZONE_RAINFOREST) { _error_message = STR_0317_CAN_ONLY_BE_BUILT_IN_RAINFOREST; return false; } return true; } static bool CheckNewIndustry_BubbleGen(TileIndex tile) { return GetTileZ(tile) <= TILE_HEIGHT * 4; } typedef bool CheckNewIndustryProc(TileIndex tile); static CheckNewIndustryProc * const _check_new_industry_procs[CHECK_END] = { CheckNewIndustry_NULL, CheckNewIndustry_Forest, CheckNewIndustry_OilRefinery, CheckNewIndustry_Farm, CheckNewIndustry_Plantation, CheckNewIndustry_Water, CheckNewIndustry_Lumbermill, CheckNewIndustry_BubbleGen, CheckNewIndustry_OilRig }; static bool CheckSuitableIndustryPos(TileIndex tile) { uint x = TileX(tile); uint y = TileY(tile); if (x < 2 || y < 2 || x > MapMaxX() - 3 || y > MapMaxY() - 3) { _error_message = STR_0239_SITE_UNSUITABLE; return false; } return true; } static const Town *CheckMultipleIndustryInTown(TileIndex tile, int type) { const Town *t; const Industry *i; t = ClosestTownFromTile(tile, (uint)-1); if (_patches.multiple_industry_per_town) return t; FOR_ALL_INDUSTRIES(i) { if (i->xy != 0 && i->type == (byte)type && i->town == t) { _error_message = STR_0287_ONLY_ONE_ALLOWED_PER_TOWN; return NULL; } } return t; } static const byte _industry_section_bits[] = { 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 4, 2, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 4, 2, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, }; static bool CheckIfIndustryTilesAreFree(TileIndex tile, const IndustryTileTable *it, int type) { _error_message = STR_0239_SITE_UNSUITABLE; do { TileIndex cur_tile = tile + ToTileIndexDiff(it->ti); if (!IsValidTile(cur_tile)) { if (it->gfx == 0xff) continue; return false; } if (it->gfx == 0xFF) { if (!IsTileType(cur_tile, MP_WATER) || GetTileSlope(cur_tile, NULL) != SLOPE_FLAT) { return false; } } else { if (!EnsureNoVehicle(cur_tile)) return false; if (type == IT_OIL_RIG) { if (!IsTileType(cur_tile, MP_WATER) || _m[cur_tile].m5 != 0) return false; } else { Slope tileh; if (IsTileType(cur_tile, MP_WATER) && _m[cur_tile].m5 == 0) return false; tileh = GetTileSlope(cur_tile, NULL); if (IsSteepSlope(tileh)) return false; if (_patches.land_generator != LG_TERRAGENESIS || !_generating_world) { /* It is almost impossible to have a fully flat land in TG, so what we * do is that we check if we can make the land flat later on. See * CheckIfCanLevelIndustryPlatform(). */ if (tileh != SLOPE_FLAT) { Slope t; byte bits = _industry_section_bits[it->gfx]; if (bits & 0x10) return false; t = ComplementSlope(tileh); if (bits & 1 && (t & SLOPE_NW)) return false; if (bits & 2 && (t & SLOPE_NE)) return false; if (bits & 4 && (t & SLOPE_SW)) return false; if (bits & 8 && (t & SLOPE_SE)) return false; } } if (type == IT_BANK_TEMP) { if (!IsTileType(cur_tile, MP_HOUSE)) { _error_message = STR_029D_CAN_ONLY_BE_BUILT_IN_TOWNS; return false; } } else if (type == IT_BANK_TROPIC_ARCTIC) { if (!IsTileType(cur_tile, MP_HOUSE)) { _error_message = STR_030D_CAN_ONLY_BE_BUILT_IN_TOWNS; return false; } } else if (type == IT_TOY_SHOP) { if (!IsTileType(cur_tile, MP_HOUSE)) goto do_clear; } else if (type == IT_WATER_TOWER) { if (!IsTileType(cur_tile, MP_HOUSE)) { _error_message = STR_0316_CAN_ONLY_BE_BUILT_IN_TOWNS; return false; } } else { do_clear: if (CmdFailed(DoCommand(cur_tile, 0, 0, DC_AUTO, CMD_LANDSCAPE_CLEAR))) return false; } } } } while ((++it)->ti.x != -0x80); return true; } static bool CheckIfIndustryIsAllowed(TileIndex tile, int type, const Town *t) { if (type == IT_BANK_TEMP && t->population < 1200) { _error_message = STR_029D_CAN_ONLY_BE_BUILT_IN_TOWNS; return false; } if (type == IT_TOY_SHOP && DistanceMax(t->xy, tile) > 9) { _error_message = STR_0239_SITE_UNSUITABLE; return false; } return true; } static bool CheckCanTerraformSurroundingTiles(TileIndex tile, uint height, int internal) { int size_x, size_y; uint curh; size_x = 2; size_y = 2; /* Check if we don't leave the map */ if (TileX(tile) == 0 || TileY(tile) == 0 || GetTileType(tile) == MP_VOID) return false; tile += TileDiffXY(-1, -1); BEGIN_TILE_LOOP(tile_walk, size_x, size_y, tile) { curh = TileHeight(tile_walk); /* Is the tile clear? */ if ((GetTileType(tile_walk) != MP_CLEAR) && (GetTileType(tile_walk) != MP_TREES)) return false; /* Don't allow too big of a change if this is the sub-tile check */ if (internal != 0 && myabs(curh - height) > 1) return false; /* Different height, so the surrounding tiles of this tile * has to be correct too (in level, or almost in level) * else you get a chain-reaction of terraforming. */ if (internal == 0 && curh != height) { if (!CheckCanTerraformSurroundingTiles(tile_walk + TileDiffXY(-1, -1), height, internal + 1)) return false; } } END_TILE_LOOP(tile_walk, size_x, size_y, tile); return true; } /** * This function tries to flatten out the land below an industry, without * damaging the surroundings too much. */ static bool CheckIfCanLevelIndustryPlatform(TileIndex tile, uint32 flags, const IndustryTileTable* it, int type) { const int MKEND = -0x80; // used for last element in an IndustryTileTable (see build_industry.h) int max_x = 0; int max_y = 0; TileIndex cur_tile; uint size_x, size_y; uint h, curh; /* Finds dimensions of largest variant of this industry */ do { if (it->ti.x > max_x) max_x = it->ti.x; if (it->ti.y > max_y) max_y = it->ti.y; } while ((++it)->ti.x != MKEND); /* Remember level height */ h = TileHeight(tile); /* Check that all tiles in area and surrounding are clear * this determines that there are no obstructing items */ cur_tile = tile + TileDiffXY(-1, -1); size_x = max_x + 4; size_y = max_y + 4; /* Check if we don't leave the map */ if (TileX(cur_tile) == 0 || TileY(cur_tile) == 0 || TileX(cur_tile) + size_x >= MapMaxX() || TileY(cur_tile) + size_y >= MapMaxY()) return false; BEGIN_TILE_LOOP(tile_walk, size_x, size_y, cur_tile) { curh = TileHeight(tile_walk); if (curh != h) { /* This tile needs terraforming. Check if we can do that without * damaging the surroundings too much. */ if (!CheckCanTerraformSurroundingTiles(tile_walk, h, 0)) return false; /* This is not 100% correct check, but the best we can do without modifying the map. * What is missing, is if the difference in height is more than 1.. */ if (CmdFailed(DoCommand(tile_walk, 8, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND))) return false; } } END_TILE_LOOP(tile_walk, size_x, size_y, cur_tile) if (flags & DC_EXEC) { /* Terraform the land under the industry */ BEGIN_TILE_LOOP(tile_walk, size_x, size_y, cur_tile) { curh = TileHeight(tile_walk); while (curh != h) { /* We give the terraforming for free here, because we can't calculate * exact cost in the test-round, and as we all know, that will cause * a nice assert if they don't match ;) */ DoCommand(tile_walk, 8, (curh > h) ? 0 : 1, flags, CMD_TERRAFORM_LAND); curh += (curh > h) ? -1 : 1; } } END_TILE_LOOP(tile_walk, size_x, size_y, cur_tile) } return true; } static bool CheckIfTooCloseToIndustry(TileIndex tile, int type) { const IndustrySpec *indspec = GetIndustrySpec(type); const Industry *i; // accepting industries won't be close, not even with patch if (_patches.same_industry_close && indspec->accepts_cargo[0] == CT_INVALID) return true; FOR_ALL_INDUSTRIES(i) { // check if an industry that accepts the same goods is nearby if (i->xy != 0 && DistanceMax(tile, i->xy) <= 14 && indspec->accepts_cargo[0] != CT_INVALID && indspec->accepts_cargo[0] == i->accepts_cargo[0] && ( _game_mode != GM_EDITOR || !_patches.same_industry_close || !_patches.multiple_industry_per_town )) { _error_message = STR_INDUSTRY_TOO_CLOSE; return false; } // check "not close to" field. if (i->xy != 0 && (i->type == indspec->conflicting[0] || i->type == indspec->conflicting[1] || i->type == indspec->conflicting[2]) && DistanceMax(tile, i->xy) <= 14) { _error_message = STR_INDUSTRY_TOO_CLOSE; return false; } } return true; } static Industry *AllocateIndustry(void) { Industry *i; FOR_ALL_INDUSTRIES(i) { if (i->xy == 0) { uint index = i->index; if (i->index > _total_industries) _total_industries = i->index; memset(i, 0, sizeof(*i)); i->index = index; return i; } } /* Check if we can add a block to the pool */ return AddBlockToPool(&_industry_pool) ? AllocateIndustry() : NULL; } static void DoCreateNewIndustry(Industry *i, TileIndex tile, int type, const IndustryTileTable *it, const Town *t, byte owner) { const IndustrySpec *indspec = GetIndustrySpec(type); uint32 r; int j; i->xy = tile; i->width = i->height = 0; i->type = type; i->produced_cargo[0] = indspec->produced_cargo[0]; i->produced_cargo[1] = indspec->produced_cargo[1]; i->accepts_cargo[0] = indspec->accepts_cargo[0]; i->accepts_cargo[1] = indspec->accepts_cargo[1]; i->accepts_cargo[2] = indspec->accepts_cargo[2]; i->production_rate[0] = indspec->production_rate[0]; i->production_rate[1] = indspec->production_rate[1]; if (_patches.smooth_economy) { i->production_rate[0] = min((RandomRange(256) + 128) * i->production_rate[0] >> 8 , 255); i->production_rate[1] = min((RandomRange(256) + 128) * i->production_rate[1] >> 8 , 255); } i->town = t; i->owner = owner; r = Random(); i->color_map = GB(r, 8, 4); i->counter = GB(r, 0, 12); i->cargo_waiting[0] = 0; i->cargo_waiting[1] = 0; i->last_mo_production[0] = 0; i->last_mo_production[1] = 0; i->last_mo_transported[0] = 0; i->last_mo_transported[1] = 0; i->pct_transported[0] = 0; i->pct_transported[1] = 0; i->total_transported[0] = 0; i->total_transported[1] = 0; i->was_cargo_delivered = false; i->last_prod_year = _cur_year - BASE_YEAR; i->total_production[0] = i->production_rate[0] * 8; i->total_production[1] = i->production_rate[1] * 8; if (!_generating_world) i->total_production[0] = i->total_production[1] = 0; i->prod_level = 0x10; do { TileIndex cur_tile = tile + ToTileIndexDiff(it->ti); if (it->gfx != 0xFF) { byte size; size = it->ti.x; if (size > i->width) i->width = size; size = it->ti.y; if (size > i->height)i->height = size; DoCommand(cur_tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR); MakeIndustry(cur_tile, i->index, it->gfx); if (_generating_world) _m[cur_tile].m1 = 0x1E; /* maturity */ } } while ((++it)->ti.x != -0x80); i->width++; i->height++; if (i->type == IT_FARM || i->type == IT_FARM_2) { tile = i->xy + TileDiffXY(i->width / 2, i->height / 2); for (j = 0; j != 50; j++) { int x = Random() % 31 - 16; int y = Random() % 31 - 16; TileIndex new_tile = TileAddWrap(tile, x, y); if (new_tile != INVALID_TILE) PlantFarmField(new_tile); } } _industry_sort_dirty = true; InvalidateWindow(WC_INDUSTRY_DIRECTORY, 0); } static Industry *CreateNewIndustryHelper(TileIndex tile, IndustryType type, uint32 flags, const IndustrySpec *indspec, const IndustryTileTable *it) { const Town *t; Industry *i; if (!CheckIfIndustryTilesAreFree(tile, it, type)) return NULL; if (_patches.land_generator == LG_TERRAGENESIS && _generating_world && !CheckIfCanLevelIndustryPlatform(tile, 0, it, type)) return NULL; if (!_check_new_industry_procs[indspec->check_proc](tile)) return NULL; if (!CheckIfTooCloseToIndustry(tile, type)) return NULL; t = CheckMultipleIndustryInTown(tile, type); if (t == NULL) return NULL; if (!CheckIfIndustryIsAllowed(tile, type, t)) return NULL; if (!CheckSuitableIndustryPos(tile)) return NULL; i = AllocateIndustry(); if (i == NULL) return NULL; if (flags & DC_EXEC) { CheckIfCanLevelIndustryPlatform(tile, DC_EXEC, it, type); DoCreateNewIndustry(i, tile, type, it, t, OWNER_NONE); } return i; } /** Build/Fund an industry * @param tile tile where industry is built * @param p1 industry type @see build_industry.h and @see industry.h * @param p2 unused */ int32 CmdBuildIndustry(TileIndex tile, uint32 flags, uint32 p1, uint32 p2) { int num; const IndustryTileTable * const *itt; const IndustryTileTable *it; const IndustrySpec *indspec; SET_EXPENSES_TYPE(EXPENSES_OTHER); /* Check if the to-be built/founded industry is available for this climate. * Unfortunately we have no easy way of checking, except for looping the table */ { const byte *i; bool found = false; for (i = &_build_industry_types[_opt_ptr->landscape][0]; i != endof(_build_industry_types[_opt_ptr->landscape]); i++) { if (*i == p1) { found = true; break; } } if (!found) return CMD_ERROR; } indspec = GetIndustrySpec(p1); /* If the patch for raw-material industries is not on, you cannot build raw-material industries. * Raw material industries are industries that do not accept cargo (at least for now) * Exclude the lumber mill (only "raw" industry that can be built) */ if (!_patches.build_rawmaterial_ind && indspec->accepts_cargo[0] == CT_INVALID && indspec->accepts_cargo[1] == CT_INVALID && indspec->accepts_cargo[2] == CT_INVALID && p1 != IT_LUMBER_MILL) { return CMD_ERROR; } num = indspec->num_table; itt = indspec->table; do { if (--num < 0) return_cmd_error(STR_0239_SITE_UNSUITABLE); } while (!CheckIfIndustryTilesAreFree(tile, it = itt[num], p1)); if (CreateNewIndustryHelper(tile, p1, flags, indspec, it) == NULL) return CMD_ERROR; return (_price.build_industry >> 5) * indspec->cost_multiplier; } Industry *CreateNewIndustry(TileIndex tile, IndustryType type) { const IndustrySpec *indspec = GetIndustrySpec(type); const IndustryTileTable *it = indspec->table[RandomRange(indspec->num_table)]; return CreateNewIndustryHelper(tile, type, DC_EXEC, indspec, it); } static const byte _numof_industry_table[4][12] = { // difficulty settings for number of industries {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, //none {0, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5}, //low {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, //normal {0, 2, 3, 4, 6, 7, 8, 9, 10, 10, 10}, //high }; static void PlaceInitialIndustry(IndustryType type, int amount) { int num = _numof_industry_table[_opt.diff.number_industries][amount]; if (type == IT_OIL_REFINERY || type == IT_OIL_RIG) { // These are always placed next to the coastline, so we scale by the perimeter instead. num = ScaleByMapSize1D(num); } else { num = ScaleByMapSize(num); } if (_opt.diff.number_industries != 0) { PlayerID old_player = _current_player; _current_player = OWNER_NONE; assert(num > 0); do { uint i; IncreaseGeneratingWorldProgress(GWP_INDUSTRY); for (i = 0; i < 2000; i++) { if (CreateNewIndustry(RandomTile(), type) != NULL) break; } } while (--num); _current_player = old_player; } } void GenerateIndustries(void) { const byte *b; uint i = 0; /* Find the total amount of industries */ b = _industry_create_table[_opt.landscape]; do { int num = _numof_industry_table[_opt.diff.number_industries][b[0]]; if (b[1] == IT_OIL_REFINERY || b[1] == IT_OIL_RIG) { /* These are always placed next to the coastline, so we scale by the perimeter instead. */ num = ScaleByMapSize1D(num); } else { num = ScaleByMapSize(num); } i += num; } while ( (b+=2)[0] != 0); SetGeneratingWorldProgress(GWP_INDUSTRY, i); b = _industry_create_table[_opt.landscape]; do { PlaceInitialIndustry(b[1], b[0]); } while ( (b+=2)[0] != 0); } /* Change industry production or do closure */ static void ExtChangeIndustryProduction(Industry *i) { bool closeit = true; int j; const IndustrySpec *indspec = GetIndustrySpec(i->type); switch (_industry_close_mode[i->type]) { case INDUSTRYLIFE_NOT_CLOSABLE: return; case INDUSTRYLIFE_CLOSABLE: if ((byte)((_cur_year - BASE_YEAR) - i->last_prod_year) < 5 || !CHANCE16(1, 180)) closeit = false; break; default: /* INDUSTRY_PRODUCTION */ for (j = 0; j < 2 && i->produced_cargo[j] != CT_INVALID; j++){ uint32 r = Random(); int old, new, percent; int mag; new = old = i->production_rate[j]; if (CHANCE16I(20, 1024, r)) new -= ((RandomRange(50) + 10) * old) >> 8; if (CHANCE16I(20 + (i->pct_transported[j] * 20 >> 8), 1024, r >> 16)) new += ((RandomRange(50) + 10) * old) >> 8; new = clamp(new, 0, 255); if (new == old) { closeit = false; continue; } percent = new * 100 / old - 100; i->production_rate[j] = new; if (new >= indspec->production_rate[j] / 4) closeit = false; mag = abs(percent); if (mag >= 10) { SetDParam(2, mag); SetDParam(0, _cargoc.names_s[i->produced_cargo[j]]); SetDParam(1, i->index); AddNewsItem( percent >= 0 ? STR_INDUSTRY_PROD_GOUP : STR_INDUSTRY_PROD_GODOWN, NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_TILE, NT_ECONOMY, 0), i->xy + TileDiffXY(1, 1), 0 ); } } break; } /* If industry will be closed down, show this */ if (closeit) { i->prod_level = 0; SetDParam(0, i->index); AddNewsItem( indspec->closure_text, NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_TILE, NT_ECONOMY, 0), i->xy + TileDiffXY(1, 1), 0 ); } } static void UpdateIndustryStatistics(Industry *i) { byte pct; if (i->produced_cargo[0] != CT_INVALID) { pct = 0; if (i->last_mo_production[0] != 0) { i->last_prod_year = _cur_year - BASE_YEAR; pct = min(i->last_mo_transported[0] * 256 / i->last_mo_production[0],255); } i->pct_transported[0] = pct; i->total_production[0] = i->last_mo_production[0]; i->last_mo_production[0] = 0; i->total_transported[0] = i->last_mo_transported[0]; i->last_mo_transported[0] = 0; } if (i->produced_cargo[1] != CT_INVALID) { pct = 0; if (i->last_mo_production[1] != 0) { i->last_prod_year = _cur_year - BASE_YEAR; pct = min(i->last_mo_transported[1] * 256 / i->last_mo_production[1],255); } i->pct_transported[1] = pct; i->total_production[1] = i->last_mo_production[1]; i->last_mo_production[1] = 0; i->total_transported[1] = i->last_mo_transported[1]; i->last_mo_transported[1] = 0; } if (i->produced_cargo[0] != CT_INVALID || i->produced_cargo[1] != CT_INVALID) InvalidateWindow(WC_INDUSTRY_VIEW, i->index); if (i->prod_level == 0) { DeleteIndustry(i); } else if (_patches.smooth_economy) { ExtChangeIndustryProduction(i); } } static const byte _new_industry_rand[4][32] = { {12,12,12,12,12,12,12, 0, 0, 6, 6, 9, 9, 3, 3, 3,18,18, 4, 4, 2, 2, 5, 5, 5, 5, 5, 5, 1, 1, 8, 8}, {16,16,16, 0, 0, 0, 9, 9, 9, 9,13,13, 3, 3, 3, 3,15,15,15, 4, 4,11,11,11,11,11,14,14, 1, 1, 7, 7}, {21,21,21,24,22,22,22,22,23,23,16,16,16, 4, 4,19,19,19,13,13,20,20,20,11,11,11,17,17,17,10,10,10}, {30,30,30,36,36,31,31,31,27,27,27,28,28,28,26,26,26,34,34,34,35,35,35,29,29,29,32,32,32,33,33,33}, }; static void MaybeNewIndustry(uint32 r) { int type; int j; Industry *i; type = _new_industry_rand[_opt.landscape][GB(r, 16, 5)]; if (type == IT_OIL_WELL && _cur_year > 1950) return; if (type == IT_OIL_RIG && _cur_year < 1960) return; j = 2000; for (;;) { i = CreateNewIndustry(RandomTile(), type); if (i != NULL) break; if (--j == 0) return; } SetDParam(0, type + STR_4802_COAL_MINE); SetDParam(1, i->town->index); AddNewsItem( (type != IT_FOREST && type != IT_FRUIT_PLANTATION && type != IT_RUBBER_PLANTATION && type != IT_COTTON_CANDY) ? STR_482D_NEW_UNDER_CONSTRUCTION : STR_482E_NEW_BEING_PLANTED_NEAR, NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_TILE, NT_ECONOMY,0), i->xy, 0 ); } static void ChangeIndustryProduction(Industry *i) { bool only_decrease = false; StringID str = STR_NULL; int type = i->type; const IndustrySpec *indspec = GetIndustrySpec(type); switch (_industry_close_mode[type]) { case INDUSTRYLIFE_NOT_CLOSABLE: return; case INDUSTRYLIFE_PRODUCTION: /* decrease or increase */ if (type == IT_OIL_WELL && _opt.landscape == LT_NORMAL) only_decrease = true; if (only_decrease || CHANCE16(1,3)) { /* If you transport > 60%, 66% chance we increase, else 33% chance we increase */ if (!only_decrease && (i->pct_transported[0] > 153) != CHANCE16(1,3)) { /* Increase production */ if (i->prod_level != 0x80) { byte b; i->prod_level <<= 1; b = i->production_rate[0] * 2; if (i->production_rate[0] >= 128) b = 0xFF; i->production_rate[0] = b; b = i->production_rate[1] * 2; if (i->production_rate[1] >= 128) b = 0xFF; i->production_rate[1] = b; str = indspec->production_up_text; } } else { /* Decrease production */ if (i->prod_level == 4) { i->prod_level = 0; str = indspec->closure_text; } else { i->prod_level >>= 1; i->production_rate[0] = (i->production_rate[0] + 1) >> 1; i->production_rate[1] = (i->production_rate[1] + 1) >> 1; str = indspec->production_down_text; } } } break; case INDUSTRYLIFE_CLOSABLE: /* maybe close */ if ( (byte)((_cur_year - BASE_YEAR) - i->last_prod_year) >= 5 && CHANCE16(1,2)) { i->prod_level = 0; str = indspec->closure_text; } break; } if (str != STR_NULL) { SetDParam(0, i->index); AddNewsItem(str, NEWS_FLAGS(NM_THIN, NF_VIEWPORT|NF_TILE, NT_ECONOMY, 0), i->xy + TileDiffXY(1, 1), 0); } } void IndustryMonthlyLoop(void) { Industry *i; PlayerID old_player = _current_player; _current_player = OWNER_NONE; FOR_ALL_INDUSTRIES(i) { if (i->xy != 0) UpdateIndustryStatistics(i); } /* 3% chance that we start a new industry */ if (CHANCE16(3, 100)) { MaybeNewIndustry(Random()); } else if (!_patches.smooth_economy && _total_industries > 0) { i = GetIndustry(RandomRange(_total_industries)); if (i->xy != 0) ChangeIndustryProduction(i); } _current_player = old_player; // production-change _industry_sort_dirty = true; InvalidateWindow(WC_INDUSTRY_DIRECTORY, 0); } void InitializeIndustries(void) { CleanPool(&_industry_pool); AddBlockToPool(&_industry_pool); _total_industries = 0; _industry_sort_dirty = true; } const TileTypeProcs _tile_type_industry_procs = { DrawTile_Industry, /* draw_tile_proc */ GetSlopeZ_Industry, /* get_slope_z_proc */ ClearTile_Industry, /* clear_tile_proc */ GetAcceptedCargo_Industry, /* get_accepted_cargo_proc */ GetTileDesc_Industry, /* get_tile_desc_proc */ GetTileTrackStatus_Industry,/* get_tile_track_status_proc */ ClickTile_Industry, /* click_tile_proc */ AnimateTile_Industry, /* animate_tile_proc */ TileLoop_Industry, /* tile_loop_proc */ ChangeTileOwner_Industry, /* change_tile_owner_proc */ GetProducedCargo_Industry, /* get_produced_cargo_proc */ NULL, /* vehicle_enter_tile_proc */ GetSlopeTileh_Industry, /* get_slope_tileh_proc */ }; static const SaveLoad _industry_desc[] = { SLE_CONDVAR(Industry, xy, SLE_FILE_U16 | SLE_VAR_U32, 0, 5), SLE_CONDVAR(Industry, xy, SLE_UINT32, 6, SL_MAX_VERSION), SLE_VAR(Industry,width, SLE_UINT8), SLE_VAR(Industry,height, SLE_UINT8), SLE_REF(Industry,town, REF_TOWN), SLE_ARR(Industry,produced_cargo, SLE_UINT8, 2), SLE_ARR(Industry,cargo_waiting, SLE_UINT16, 2), SLE_ARR(Industry,production_rate, SLE_UINT8, 2), SLE_ARR(Industry,accepts_cargo, SLE_UINT8, 3), SLE_VAR(Industry,prod_level, SLE_UINT8), SLE_ARR(Industry,last_mo_production,SLE_UINT16, 2), SLE_ARR(Industry,last_mo_transported,SLE_UINT16, 2), SLE_ARR(Industry,pct_transported,SLE_UINT8, 2), SLE_ARR(Industry,total_production,SLE_UINT16, 2), SLE_ARR(Industry,total_transported,SLE_UINT16, 2), SLE_VAR(Industry,counter, SLE_UINT16), SLE_VAR(Industry,type, SLE_UINT8), SLE_VAR(Industry,owner, SLE_UINT8), SLE_VAR(Industry,color_map, SLE_UINT8), SLE_CONDVAR(Industry, last_prod_year, SLE_FILE_U8 | SLE_VAR_I32, 0, 30), SLE_CONDVAR(Industry, last_prod_year, SLE_INT32, 31, SL_MAX_VERSION), SLE_VAR(Industry,was_cargo_delivered,SLE_UINT8), // reserve extra space in savegame here. (currently 32 bytes) SLE_CONDNULL(32, 2, SL_MAX_VERSION), SLE_END() }; static void Save_INDY(void) { Industry *ind; // Write the vehicles FOR_ALL_INDUSTRIES(ind) { if (ind->xy != 0) { SlSetArrayIndex(ind->index); SlObject(ind, _industry_desc); } } } static void Load_INDY(void) { int index; _total_industries = 0; while ((index = SlIterateArray()) != -1) { Industry *i; if (!AddBlockIfNeeded(&_industry_pool, index)) error("Industries: failed loading savegame: too many industries"); i = GetIndustry(index); SlObject(i, _industry_desc); if (index > _total_industries) _total_industries = index; } } const ChunkHandler _industry_chunk_handlers[] = { { 'INDY', Save_INDY, Load_INDY, CH_ARRAY | CH_LAST}, };