tron@2186: /* $Id$ */ tron@2186: belugas@6201: /** @file industry_cmd.cpp */ belugas@6201: truelight@0: #include "stdafx.h" Darkvater@1891: #include "openttd.h" tron@3144: #include "clear_map.h" truelight@4356: #include "functions.h" tron@3314: #include "industry_map.h" tron@3338: #include "station_map.h" tron@507: #include "table/strings.h" celestar@2148: #include "table/sprites.h" tron@679: #include "map.h" tron@1209: #include "tile.h" maedhros@6343: #include "landscape.h" truelight@0: #include "viewport.h" truelight@0: #include "command.h" truelight@0: #include "industry.h" truelight@0: #include "town.h" truelight@0: #include "vehicle.h" truelight@0: #include "news.h" truelight@0: #include "saveload.h" truelight@0: #include "economy.h" tron@337: #include "sound.h" tron@2159: #include "variables.h" belugas@3654: #include "table/industry_land.h" belugas@3654: #include "table/build_industry.h" truelight@4300: #include "genworld.h" rubidium@4261: #include "date.h" belugas@5056: #include "water_map.h" belugas@5909: #include "tree_map.h" peter1138@6091: #include "cargotype.h" truelight@0: 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@6824: const Industry **_industry_sort; belugas@6824: bool _industry_sort_dirty; 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++) { belugas@6743: _industry_specs[i].enabled = HASBIT(_origin_industry_specs[i].climate_availability, _opt.landscape); belugas@6743: } 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@6743: } belugas@6743: truelight@1267: /** truelight@1267: * Called if a new block is added to the industry-pool truelight@1267: */ tron@2817: static void IndustryPoolNewBlock(uint start_item) truelight@1267: { truelight@1267: Industry *i; truelight@1267: truelight@4346: /* We don't use FOR_ALL here, because FOR_ALL skips invalid items. truelight@4346: * TODO - This is just a temporary stage, this will be removed. */ tron@4976: for (i = GetIndustry(start_item); i != NULL; i = (i->index + 1U < GetIndustryPoolSize()) ? GetIndustry(i->index + 1U) : NULL) i->index = start_item++; truelight@1267: } truelight@1267: matthijs@5216: DEFINE_OLD_POOL(Industry, Industry, IndustryPoolNewBlock, NULL) 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); belugas@6639: return IsValidIndustry(ind) ? 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@6201: * @param gfx of industrytile (which is the index in _industry_specs) belugas@6749: * @pre gfx < INVALID_INDUSTRYTILE belugas@6201: * @return a pointer to the corresponding industrytile spec belugas@6201: **/ belugas@6092: const IndustryTileSpec *GetIndustryTileSpec(IndustryGfx gfx) belugas@6092: { belugas@6749: assert(gfx < INVALID_INDUSTRYTILE); belugas@6092: return &_industry_tile_specs[gfx]; belugas@6092: } belugas@6092: truelight@4403: void DestroyIndustry(Industry *i) truelight@4403: { truelight@4403: BEGIN_TILE_LOOP(tile_cur, i->width, i->height, i->xy); truelight@4403: if (IsTileType(tile_cur, MP_INDUSTRY)) { truelight@4403: if (GetIndustryIndex(tile_cur) == i->index) { truelight@4403: DoClearSquare(tile_cur); truelight@4403: } truelight@4403: } else if (IsTileType(tile_cur, MP_STATION) && IsOilRig(tile_cur)) { truelight@4403: DeleteOilRig(tile_cur); truelight@4403: } truelight@4403: END_TILE_LOOP(tile_cur, i->width, i->height, i->xy); truelight@4403: belugas@6390: if (GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_PLANT_FIELDS) { truelight@4403: /* Remove the farmland and convert it to regular tiles over time. */ truelight@4403: BEGIN_TILE_LOOP(tile_cur, 42, 42, i->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) && truelight@4403: GetIndustryIndexOfField(tile_cur) == i->index) { truelight@4403: SetIndustryIndexOfField(tile_cur, INVALID_INDUSTRY); truelight@4403: } truelight@4403: } END_TILE_LOOP(tile_cur, 42, 42, i->xy - TileDiff(21, 21)) truelight@4403: } truelight@4403: truelight@4403: _industry_sort_dirty = true; belugas@6824: DecIndustryTypeCount(i->type); belugas@6824: truelight@4403: DeleteSubsidyWithIndustry(i->index); truelight@4403: DeleteWindowById(WC_INDUSTRY_VIEW, i->index); truelight@4403: InvalidateWindow(WC_INDUSTRY_DIRECTORY, 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: { rubidium@5436: const IndustryGfx gfx = GetIndustryGfx(ti->tile); belugas@3662: const Industry *ind; belugas@3654: const DrawBuildingsTileStruct *dits; truelight@0: byte z; peter1138@5668: SpriteID image; peter1138@5668: SpriteID pal; truelight@0: truelight@0: /* Pointer to industry */ tron@3314: ind = GetIndustryByTile(ti->tile); truelight@0: truelight@0: /* Retrieve pointer to the draw industry tile struct */ belugas@6305: dits = &_industry_draw_tile_data[gfx << 2 | (GetIndustryTileSpec(gfx)->anim_state ? rubidium@5436: GetIndustryAnimationState(ti->tile) & 3 : rubidium@5436: GetIndustryConstructionStage(ti->tile))]; truelight@0: peter1138@5668: image = dits->ground.sprite; peter1138@5668: 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: truelight@0: z = ti->z; truelight@0: /* Add bricks below the industry? */ tron@3636: if (ti->tileh != SLOPE_FLAT) { peter1138@5668: AddSortableSpriteToDraw(SPR_FOUNDATION_BASE + ti->tileh, PAL_NONE, ti->x, ti->y, 16, 16, 7, z); peter1138@5668: AddChildSpriteScreen(image, pal, 31, 1); tron@3645: z += TILE_HEIGHT; truelight@0: } else { truelight@0: /* Else draw regular ground */ peter1138@5668: DrawGroundSprite(image, pal); truelight@0: } truelight@0: truelight@0: /* Add industry on top of the ground? */ peter1138@5668: image = dits->building.sprite; tron@2639: if (image != 0) { peter1138@6427: if (HASBIT(_transparent_opt, TO_INDUSTRIES)) { peter1138@5668: SETBIT(image, PALETTE_MODIFIER_TRANSPARENT); peter1138@5668: pal = PALETTE_TO_TRANSPARENT; belugas@6910: } else if (HASBIT(image, PALETTE_MODIFIER_COLOR) && dits->building.pal == PAL_NONE) { belugas@6910: pal = GENERAL_SPRITE_COLOR(ind->random_color); peter1138@5668: } else { peter1138@5668: pal = dits->building.pal; peter1138@5668: } truelight@201: peter1138@5668: AddSortableSpriteToDraw(image, pal, tron@2653: ti->x + dits->subtile_x, tron@2653: ti->y + dits->subtile_y, tron@2653: dits->width + 1, tron@2653: dits->height + 1, truelight@0: dits->dz, truelight@0: z); truelight@0: peter1138@6427: if (HASBIT(_transparent_opt, 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: tron@3636: static Slope GetSlopeTileh_Industry(TileIndex tile, Slope tileh) tron@2548: { tron@3636: return SLOPE_FLAT; dominik@39: } dominik@39: tron@1977: static void GetAcceptedCargo_Industry(TileIndex tile, AcceptedCargo ac) truelight@0: { belugas@6092: const IndustryTileSpec *itspec = GetIndustryTileSpec(GetIndustryGfx(tile)); tron@2598: CargoID a; truelight@0: belugas@6736: for (byte i = 0; i < lengthof(itspec->accepts_cargo); i++) { belugas@6736: a = itspec->accepts_cargo[i]; belugas@6736: if (a != CT_INVALID) ac[a] = itspec->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); truelight@0: truelight@0: td->owner = i->owner; belugas@4942: td->str = GetIndustrySpec(i->type)->name; tron@3321: if (!IsIndustryCompleted(tile)) { tron@534: SetDParamX(td->dparam, 0, td->str); truelight@0: td->str = STR_2058_UNDER_CONSTRUCTION; truelight@0: } truelight@0: } truelight@0: rubidium@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: */ darkvater@149: if ((_current_player != OWNER_WATER && _game_mode != GM_EDITOR && darkvater@149: !_cheats.magic_bulldozer.value) || belugas@6390: (_current_player == OWNER_WATER && (indspec->behaviour & INDUSTRYBEH_BUILT_ONWATER))) { belugas@6390: SetDParam(0, indspec->name); truelight@0: return_cmd_error(STR_4800_IN_THE_WAY); truelight@0: } truelight@0: tron@2639: if (flags & DC_EXEC) DeleteIndustry(i); truelight@0: return 0; 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); truelight@0: uint cw, am; truelight@0: tron@2639: cw = min(i->cargo_waiting[0], 255); belugas@3669: if (cw > indspec->minimal_cargo/* && i->produced_cargo[0] != 0xFF*/) { truelight@0: i->cargo_waiting[0] -= cw; truelight@0: truelight@0: /* fluctuating economy? */ tron@2639: if (_economy.fluct <= 0) cw = (cw + 1) / 2; truelight@0: rubidium@6819: i->this_month_production[0] += cw; truelight@0: belugas@6636: am = MoveGoodsToStation(i->xy, i->width, i->height, indspec->produced_cargo[0], cw); rubidium@6819: i->this_month_transported[0] += am; tron@3331: if (am != 0) { belugas@6305: uint newgfx = GetIndustryTileSpec(GetIndustryGfx(tile))->anim_production; tron@3331: belugas@6743: if (newgfx != INDUSTRYTILE_NOANIM) { belugas@3495: ResetIndustryConstructionStage(tile); belugas@3495: SetIndustryCompleted(tile, true); tron@3331: SetIndustryGfx(tile, newgfx); tron@3331: MarkTileDirtyByTile(tile); tron@3331: } truelight@0: } truelight@0: } truelight@0: truelight@0: cw = min(i->cargo_waiting[1], 255); belugas@3669: if (cw > indspec->minimal_cargo) { truelight@0: i->cargo_waiting[1] -= cw; truelight@201: tron@2639: if (_economy.fluct <= 0) cw = (cw + 1) / 2; truelight@201: rubidium@6819: i->this_month_production[1] += cw; truelight@0: belugas@6636: am = MoveGoodsToStation(i->xy, i->width, i->height, indspec->produced_cargo[1], cw); rubidium@6819: i->this_month_transported[1] += am; truelight@0: } truelight@0: } truelight@0: truelight@0: tron@1977: static void AnimateTile_Industry(TileIndex tile) truelight@0: { tron@3331: byte m; truelight@0: tron@3331: switch (GetIndustryGfx(tile)) { 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) { rubidium@6491: 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: belugas@3290: static void CreateIndustryEffectSmoke(TileIndex tile) truelight@0: { tron@3773: uint x = TileX(tile) * TILE_SIZE; tron@3773: uint y = TileY(tile) * TILE_SIZE; tron@3773: uint z = GetTileMaxZ(tile); tron@3094: tron@3773: CreateEffectVehicle(x + 15, y + 14, z + 59, EV_CHIMNEY_SMOKE); truelight@0: } truelight@0: belugas@3495: static void MakeIndustryTileBigger(TileIndex tile) truelight@0: { belugas@3495: byte cnt = GetIndustryConstructionCounter(tile) + 1; belugas@3495: byte stage; truelight@0: belugas@3495: if (cnt != 4) { belugas@3495: SetIndustryConstructionCounter(tile, cnt); truelight@0: return; truelight@0: } truelight@0: belugas@3495: stage = GetIndustryConstructionStage(tile) + 1; belugas@3495: SetIndustryConstructionCounter(tile, 0); belugas@3495: SetIndustryConstructionStage(tile, stage); belugas@3495: if (stage == 3) { belugas@3495: SetIndustryCompleted(tile, true); belugas@3495: } truelight@201: truelight@0: MarkTileDirtyByTile(tile); truelight@0: tron@3321: if (!IsIndustryCompleted(tile)) return; truelight@0: tron@3331: switch (GetIndustryGfx(tile)) { rubidium@4583: case GFX_POWERPLANT_CHIMNEY: belugas@3290: CreateIndustryEffectSmoke(tile); truelight@0: break; truelight@0: peter1138@4607: case GFX_OILRIG_1: peter1138@4607: if (GetIndustryGfx(tile + TileDiffXY(0, 1)) == GFX_OILRIG_1) BuildOilRig(tile); truelight@0: break; truelight@0: belugas@3545: case GFX_TOY_FACTORY: belugas@3545: case GFX_BUBBLE_CATCHER: belugas@3545: case GFX_TOFFEE_QUARY: rubidium@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: truelight@0: belugas@3290: static void TileLoopIndustry_BubbleGenerator(TileIndex tile) truelight@0: { truelight@0: int dir; truelight@0: Vehicle *v; truelight@0: static const int8 _tileloop_ind_case_161[12] = { rubidium@4344: 11, 0, -4, -14, rubidium@4344: -4, -10, -4, 1, rubidium@4344: 49, 59, 60, 65, truelight@0: }; truelight@0: tron@541: SndPlayTileFx(SND_2E_EXTRACT_AND_POP, tile); truelight@201: truelight@0: dir = Random() & 3; truelight@0: truelight@0: v = CreateEffectVehicleAbove( celestar@3421: TileX(tile) * TILE_SIZE + _tileloop_ind_case_161[dir + 0], celestar@3421: TileY(tile) * TILE_SIZE + _tileloop_ind_case_161[dir + 4], truelight@0: _tileloop_ind_case_161[dir + 8], tron@1359: EV_BUBBLE truelight@0: ); truelight@0: tron@2639: if (v != NULL) v->u.special.unk2 = 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: 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: 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_OILRIG_1: // coast line at oilrigs rubidium@4583: case GFX_OILRIG_2: rubidium@4583: case GFX_OILRIG_3: rubidium@4583: case GFX_OILRIG_4: rubidium@4583: case GFX_OILRIG_5: dominik@43: TileLoop_Water(tile); dominik@43: break; dominik@43: rubidium@4583: case GFX_COAL_MINE_TOWER_NOT_ANIMATED: rubidium@4583: case GFX_COPPER_MINE_TOWER_NOT_ANIMATED: rubidium@4583: case GFX_GOLD_MINE_TOWER_NOT_ANIMATED: rubidium@5435: 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: rubidium@5435: 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: rubidium@6491: 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: tron@2639: if (CHANCE16(1, 3)) AddAnimatedTile(tile); truelight@0: break; truelight@0: } truelight@0: } truelight@0: truelight@0: tron@1977: static void ClickTile_Industry(TileIndex tile) truelight@0: { tron@3314: ShowIndustryViewWindow(GetIndustryIndex(tile)); truelight@0: } truelight@0: rubidium@6683: static uint32 GetTileTrackStatus_Industry(TileIndex tile, TransportType mode, uint sub_mode) truelight@0: { truelight@0: return 0; truelight@0: } truelight@0: Darkvater@3344: static void GetProducedCargo_Industry(TileIndex tile, CargoID *b) truelight@0: { belugas@6636: const IndustrySpec *i = GetIndustrySpec(GetIndustryByTile(tile)->type); tron@2630: truelight@0: b[0] = i->produced_cargo[0]; truelight@0: b[1] = i->produced_cargo[1]; truelight@0: } truelight@0: Darkvater@2436: static void ChangeTileOwner_Industry(TileIndex tile, PlayerID old_player, PlayerID new_player) truelight@0: { truelight@0: /* not used */ 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); tron@2955: case MP_TREES: return false; 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); tron@2955: case MP_TREES: return false; 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: rubidium@5587: 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: belugas@6357: if (_opt.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; belugas@6357: if (_opt.landscape == LT_ARCTIC) r += 0x404; tron@2150: size_x = GB(r, 0, 8); tron@2150: size_y = GB(r, 8, 8); truelight@0: truelight@0: /* offset tile to match size */ tron@1981: tile -= TileDiffXY(size_x / 2, size_y / 2); truelight@201: truelight@0: /* check the amount of bad tiles */ truelight@0: count = 0; truelight@0: BEGIN_TILE_LOOP(cur_tile, size_x, size_y, tile) truelight@0: cur_tile = TILE_MASK(cur_tile); truelight@0: count += IsBadFarmFieldTile(cur_tile); truelight@0: END_TILE_LOOP(cur_tile, size_x, size_y, tile) tron@2133: if (count * 2 >= size_x * size_y) return; truelight@0: truelight@0: /* determine type of field */ truelight@0: r = Random(); tron@2979: counter = GB(r, 5, 3); tron@2979: field_type = GB(r, 8, 8) * 9 >> 8; truelight@0: truelight@0: /* make field */ truelight@0: BEGIN_TILE_LOOP(cur_tile, size_x, size_y, tile) truelight@0: cur_tile = TILE_MASK(cur_tile); truelight@0: if (!IsBadFarmFieldTile2(cur_tile)) { truelight@4328: MakeField(cur_tile, field_type, industry); tron@2979: SetClearCounter(cur_tile, counter); tron@2979: MarkTileDirtyByTile(cur_tile); truelight@0: } truelight@0: END_TILE_LOOP(cur_tile, size_x, size_y, tile) truelight@201: truelight@0: type = 3; belugas@6357: if (_opt.landscape != LT_ARCTIC && _opt.landscape != LT_TROPIC) { truelight@0: type = _plantfarmfield_type[Random() & 0xF]; truelight@0: } truelight@0: tron@3157: SetupFarmFieldFence(tile - TileDiffXY(1, 0), size_y, type, AXIS_Y); tron@3157: SetupFarmFieldFence(tile - TileDiffXY(0, 1), size_x, type, AXIS_X); tron@3157: SetupFarmFieldFence(tile + TileDiffXY(size_x - 1, 0), size_y, type, AXIS_Y); tron@3157: SetupFarmFieldFence(tile + TileDiffXY(0, size_y - 1), size_x, type, AXIS_X); truelight@0: } truelight@0: truelight@4328: void PlantRandomFarmField(const Industry *i) truelight@4328: { truelight@4328: int x = i->width / 2 + Random() % 31 - 16; truelight@4328: int y = i->height / 2 + Random() % 31 - 16; truelight@4328: truelight@4328: TileIndex tile = TileAddWrap(i->xy, x, y); truelight@4328: truelight@4328: if (tile != INVALID_TILE) PlantFarmField(tile, i->index); truelight@4328: } truelight@4328: belugas@3662: static void MaybePlantFarmField(const Industry *i) truelight@0: { truelight@4328: if (CHANCE16(1, 8)) PlantRandomFarmField(i); truelight@0: } truelight@0: belugas@5118: /** belugas@5118: * Search callback function for ChopLumberMillTrees belugas@5118: * @param tile to test belugas@5118: * @param data that is passed by the caller. In this case, nothing belugas@6201: * @return the result of the test belugas@5118: */ belugas@5118: static bool SearchLumberMillTrees(TileIndex tile, uint32 data) belugas@5118: { belugas@5909: if (IsTileType(tile, MP_TREES) && GetTreeGrowth(tile) > 2) { ///< 3 and up means all fully grown trees belugas@5118: PlayerID old_player = _current_player; belugas@5118: /* found a tree */ belugas@5118: belugas@5118: _current_player = OWNER_NONE; belugas@5118: _industry_sound_ctr = 1; belugas@5118: _industry_sound_tile = tile; belugas@5118: SndPlayTileFx(SND_38_CHAINSAW, tile); belugas@5118: belugas@5118: DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR); belugas@5118: belugas@5118: _current_player = old_player; belugas@5118: return true; belugas@5118: } belugas@5118: return false; belugas@5118: } belugas@5118: belugas@5118: /** belugas@5118: * Perform a circular search around the Lumber Mill in order to find trees to cut belugas@5118: * @param i industry belugas@5118: */ truelight@0: static void ChopLumberMillTrees(Industry *i) truelight@0: { tron@1977: TileIndex tile = i->xy; tron@2639: belugas@5118: if (!IsIndustryCompleted(tile)) return; ///< Can't proceed if not completed truelight@0: belugas@5118: if (CircularTileSearch(tile, 40, SearchLumberMillTrees, 0)) ///< 40x40 tiles to search belugas@5118: i->cargo_waiting[0] = min(0xffff, i->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) { belugas@6655: 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) { belugas@6655: IndustyBehaviour indbehav = indsp->behaviour; truelight@0: i->cargo_waiting[0] = min(0xffff, i->cargo_waiting[0] + i->production_rate[0]); truelight@0: i->cargo_waiting[1] = min(0xffff, i->cargo_waiting[1] + i->production_rate[1]); truelight@0: belugas@6390: if (indbehav & INDUSTRYBEH_PLANT_FIELDS) { truelight@0: MaybePlantFarmField(i); belugas@6390: } else if ((indbehav & INDUSTRYBEH_CUT_TREES) && (i->counter & 0x1FF) == 0) { truelight@0: ChopLumberMillTrees(i); tron@2639: } 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: truelight@0: tron@3877: static bool CheckNewIndustry_NULL(TileIndex tile) truelight@0: { truelight@0: return true; truelight@0: } truelight@0: tron@3877: static bool CheckNewIndustry_Forest(TileIndex tile) truelight@0: { belugas@6357: if (_opt.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; truelight@4300: if (DistanceFromEdge(TILE_ADDXY(tile, 1, 1)) < _patches.oil_refinery_limit) return true; tron@3877: tron@3877: _error_message = STR_483B_CAN_ONLY_BE_POSITIONED; tron@3877: return false; tron@3877: } tron@3877: dominik@7: extern bool _ignore_restrictions; dominik@7: tron@3877: static bool CheckNewIndustry_OilRig(TileIndex tile) truelight@0: { tron@2639: if (_game_mode == GM_EDITOR && _ignore_restrictions) return true; tron@3877: if (TileHeight(tile) == 0 && truelight@4300: DistanceFromEdge(TILE_ADDXY(tile, 1, 1)) < _patches.oil_refinery_limit) return true; truelight@0: truelight@0: _error_message = STR_483B_CAN_ONLY_BE_POSITIONED; truelight@0: return false; truelight@0: } truelight@0: tron@3877: static bool CheckNewIndustry_Farm(TileIndex tile) truelight@0: { belugas@6357: if (_opt.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: truelight@0: t = ClosestTownFromTile(tile, (uint)-1); truelight@201: tron@2639: if (_patches.multiple_industry_per_town) return t; truelight@0: truelight@830: FOR_ALL_INDUSTRIES(i) { truelight@4346: if (i->type == (byte)type && truelight@0: i->town == t) { truelight@0: _error_message = STR_0287_ONLY_ONE_ALLOWED_PER_TOWN; truelight@0: return NULL; truelight@0: } truelight@0: } truelight@0: truelight@0: return t; truelight@0: } truelight@0: truelight@4300: static bool CheckIfIndustryTilesAreFree(TileIndex tile, const IndustryTileTable *it, int type) truelight@0: { truelight@0: _error_message = STR_0239_SITE_UNSUITABLE; truelight@0: truelight@0: do { tron@1977: TileIndex cur_tile = tile + ToTileIndexDiff(it->ti); tron@1977: truelight@0: if (!IsValidTile(cur_tile)) { tron@3440: if (it->gfx == 0xff) continue; truelight@0: return false; truelight@0: } truelight@201: tron@3440: if (it->gfx == 0xFF) { 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 { tron@2639: if (!EnsureNoVehicle(cur_tile)) return false; rubidium@6893: if (MayHaveBridgeAbove(cur_tile) && IsBridgeAbove(cur_tile)) return false; rubidium@6893: belugas@6390: IndustyBehaviour ind_behav = GetIndustrySpec(type)->behaviour; truelight@0: belugas@6390: if (ind_behav & INDUSTRYBEH_BUILT_ONWATER) { belugas@6438: /* As soon as the tile is not water, bail out. belugas@6438: * But that does not mean the search is over. You have belugas@6438: * to make sure every tile of the industry will be only water*/ belugas@6438: if (!IsClearWaterTile(cur_tile)) return false; truelight@0: } else { tron@3636: Slope tileh; truelight@0: belugas@5056: if (IsClearWaterTile(cur_tile)) return false; tron@3296: tron@3296: tileh = GetTileSlope(cur_tile, NULL); tron@3636: if (IsSteepSlope(tileh)) return false; tron@3296: truelight@4301: if (_patches.land_generator != LG_TERRAGENESIS || !_generating_world) { truelight@4300: /* It is almost impossible to have a fully flat land in TG, so what we truelight@4300: * do is that we check if we can make the land flat later on. See truelight@4300: * CheckIfCanLevelIndustryPlatform(). */ truelight@4300: if (tileh != SLOPE_FLAT) { truelight@4300: Slope t; belugas@6092: byte bits = GetIndustryTileSpec(it->gfx)->slopes_refused; truelight@201: truelight@4300: if (bits & 0x10) return false; truelight@0: truelight@4300: t = ComplementSlope(tileh); truelight@4300: truelight@4300: if (bits & 1 && (t & SLOPE_NW)) return false; truelight@4300: if (bits & 2 && (t & SLOPE_NE)) return false; truelight@4300: if (bits & 4 && (t & SLOPE_SW)) return false; truelight@4300: if (bits & 8 && (t & SLOPE_SE)) return false; truelight@4300: } truelight@0: } truelight@0: belugas@6390: if (ind_behav & INDUSTRYBEH_ONLY_INTOWN) { truelight@4300: if (!IsTileType(cur_tile, MP_HOUSE)) { truelight@0: _error_message = STR_029D_CAN_ONLY_BE_BUILT_IN_TOWNS; truelight@0: return false; truelight@0: } belugas@6390: } else { belugas@6390: if (ind_behav & INDUSTRYBEH_ONLY_NEARTOWN) { belugas@6390: if (!IsTileType(cur_tile, MP_HOUSE)) goto do_clear; belugas@6390: } else { belugas@6390: do_clear: belugas@6390: if (CmdFailed(DoCommand(cur_tile, 0, 0, DC_AUTO, CMD_LANDSCAPE_CLEAR))) belugas@6390: return false; truelight@0: } truelight@0: } truelight@0: } truelight@0: } tron@909: } while ((++it)->ti.x != -0x80); truelight@0: truelight@0: return true; 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 */ rubidium@5587: 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 { 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: 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. */ truelight@4300: if (!CheckCanTerraformSurroundingTiles(tile_walk, h, 0)) return false; 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.. */ truelight@4300: if (CmdFailed(DoCommand(tile_walk, 8, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND))) return false; 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 ;) */ truelight@4300: DoCommand(tile_walk, 8, (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: truelight@4300: return true; truelight@4300: } truelight@4300: truelight@4300: tron@1977: static bool CheckIfTooCloseToIndustry(TileIndex tile, int type) truelight@0: { belugas@3669: const IndustrySpec *indspec = GetIndustrySpec(type); belugas@3662: const Industry *i; truelight@0: belugas@6201: /* accepting industries won't be close, not even with patch */ belugas@3669: if (_patches.same_industry_close && indspec->accepts_cargo[0] == CT_INVALID) truelight@0: return true; truelight@201: truelight@830: FOR_ALL_INDUSTRIES(i) { belugas@6201: /* check if an industry that accepts the same goods is nearby */ truelight@4346: if (DistanceMax(tile, i->xy) <= 14 && belugas@3669: indspec->accepts_cargo[0] != CT_INVALID && belugas@6859: indspec->accepts_cargo[0] == GetIndustrySpec(i->type)->accepts_cargo[0] && ( tron@2639: _game_mode != GM_EDITOR || tron@2639: !_patches.same_industry_close || tron@2639: !_patches.multiple_industry_per_town tron@2639: )) { tron@2639: _error_message = STR_INDUSTRY_TOO_CLOSE; tron@2639: return false; tron@2639: } truelight@0: belugas@6201: /* check "not close to" field. */ truelight@4346: if ((i->type == indspec->conflicting[0] || i->type == indspec->conflicting[1] || i->type == indspec->conflicting[2]) && tron@1245: DistanceMax(tile, i->xy) <= 14) { truelight@0: _error_message = STR_INDUSTRY_TOO_CLOSE; truelight@0: return false; truelight@0: } truelight@0: } truelight@0: return true; truelight@0: } truelight@0: rubidium@6247: static Industry *AllocateIndustry() truelight@0: { truelight@0: Industry *i; truelight@0: truelight@4346: /* We don't use FOR_ALL here, because FOR_ALL skips invalid items. truelight@4346: * TODO - This is just a temporary stage, this will be removed. */ tron@4976: for (i = GetIndustry(0); i != NULL; i = (i->index + 1U < GetIndustryPoolSize()) ? GetIndustry(i->index + 1U) : NULL) { truelight@4346: IndustryID index = i->index; truelight@1267: truelight@4346: if (IsValidIndustry(i)) continue; truelight@919: truelight@4346: memset(i, 0, sizeof(*i)); truelight@4346: i->index = index; truelight@4346: truelight@4346: return i; truelight@0: } truelight@1267: truelight@1267: /* Check if we can add a block to the pool */ tron@4976: return AddBlockToPool(&_Industry_pool) ? AllocateIndustry() : NULL; truelight@0: } truelight@0: rubidium@5587: static void DoCreateNewIndustry(Industry *i, TileIndex tile, int type, const IndustryTileTable *it, const Town *t, Owner owner) truelight@0: { belugas@3669: const IndustrySpec *indspec = GetIndustrySpec(type); truelight@0: uint32 r; truelight@0: int 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: belugas@3669: i->production_rate[0] = indspec->production_rate[0]; belugas@3669: i->production_rate[1] = indspec->production_rate[1]; truelight@0: truelight@0: if (_patches.smooth_economy) { 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(); belugas@4942: i->random_color = GB(r, 8, 4); peter1138@2492: i->counter = GB(r, 0, 12); truelight@0: i->cargo_waiting[0] = 0; truelight@0: i->cargo_waiting[1] = 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; truelight@0: rubidium@6819: if (!_generating_world) i->last_month_production[0] = i->last_month_production[1] = 0; truelight@201: truelight@0: i->prod_level = 0x10; truelight@0: truelight@0: do { tron@1977: TileIndex cur_tile = tile + ToTileIndexDiff(it->ti); truelight@0: tron@3440: if (it->gfx != 0xFF) { truelight@0: byte size; truelight@0: tron@909: size = it->ti.x; truelight@0: if (size > i->width) i->width = size; tron@909: size = it->ti.y; truelight@0: if (size > i->height)i->height = size; truelight@0: tron@3491: DoCommand(cur_tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR); truelight@0: tron@3440: MakeIndustry(cur_tile, i->index, it->gfx); rubidium@5435: if (_generating_world) { rubidium@5435: SetIndustryConstructionCounter(cur_tile, 3); rubidium@5435: SetIndustryConstructionStage(cur_tile, 2); rubidium@5435: } truelight@0: } tron@909: } while ((++it)->ti.x != -0x80); 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: } truelight@0: _industry_sort_dirty = true; truelight@0: InvalidateWindow(WC_INDUSTRY_DIRECTORY, 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 belugas@6201: * @param it pointer to list of tile type to build belugas@6201: * @return the pointer of the newly created industry, or NULL if it failed belugas@6201: */ truelight@4300: static Industry *CreateNewIndustryHelper(TileIndex tile, IndustryType type, uint32 flags, const IndustrySpec *indspec, const IndustryTileTable *it) truelight@4300: { truelight@4300: const Town *t; truelight@4300: Industry *i; truelight@4300: truelight@4300: if (!CheckIfIndustryTilesAreFree(tile, it, type)) return NULL; truelight@4300: if (_patches.land_generator == LG_TERRAGENESIS && _generating_world && !CheckIfCanLevelIndustryPlatform(tile, 0, it, type)) return NULL; truelight@4300: if (!_check_new_industry_procs[indspec->check_proc](tile)) return NULL; truelight@4300: if (!CheckIfTooCloseToIndustry(tile, type)) return NULL; truelight@4300: truelight@4300: 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: truelight@4300: i = AllocateIndustry(); truelight@4300: if (i == NULL) return NULL; truelight@4300: truelight@4300: if (flags & DC_EXEC) { truelight@4300: CheckIfCanLevelIndustryPlatform(tile, DC_EXEC, it, type); truelight@4300: DoCreateNewIndustry(i, tile, type, it, t, OWNER_NONE); truelight@4300: } truelight@4300: truelight@4300: return i; truelight@4300: } truelight@4300: Darkvater@1786: /** Build/Fund an industry tron@3491: * @param tile tile where industry is built belugas@6201: * @param flags of operations to conduct belugas@6201: * @param p1 industry type see build_industry.h and see industry.h Darkvater@1786: * @param p2 unused 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: { truelight@0: int num; truelight@0: const IndustryTileTable * const *itt; truelight@0: const IndustryTileTable *it; belugas@3669: const IndustrySpec *indspec; truelight@0: truelight@0: SET_EXPENSES_TYPE(EXPENSES_OTHER); truelight@0: belugas@4965: indspec = GetIndustrySpec(p1); 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. Darkvater@1800: * Raw material industries are industries that do not accept cargo (at least for now) tron@2638: * Exclude the lumber mill (only "raw" industry that can be built) */ tron@2638: if (!_patches.build_rawmaterial_ind && belugas@3669: indspec->accepts_cargo[0] == CT_INVALID && belugas@3669: indspec->accepts_cargo[1] == CT_INVALID && belugas@3669: indspec->accepts_cargo[2] == CT_INVALID && rubidium@6758: !(indspec->behaviour & INDUSTRYBEH_CUT_TREES)) { tron@2638: return CMD_ERROR; tron@2638: } truelight@0: belugas@3669: num = indspec->num_table; belugas@3669: itt = indspec->table; truelight@0: truelight@0: do { Darkvater@1786: if (--num < 0) return_cmd_error(STR_0239_SITE_UNSUITABLE); truelight@4300: } while (!CheckIfIndustryTilesAreFree(tile, it = itt[num], p1)); truelight@0: truelight@4300: if (CreateNewIndustryHelper(tile, p1, flags, indspec, it) == NULL) return CMD_ERROR; truelight@201: rubidium@6815: return (_price.build_industry >> 8) * indspec->cost_multiplier; truelight@0: } truelight@0: truelight@0: belugas@3538: Industry *CreateNewIndustry(TileIndex tile, IndustryType type) truelight@0: { truelight@4300: const IndustrySpec *indspec = GetIndustrySpec(type); truelight@4300: const IndustryTileTable *it = indspec->table[RandomRange(indspec->num_table)]; truelight@0: truelight@4300: return CreateNewIndustryHelper(tile, type, DC_EXEC, indspec, it); truelight@0: } truelight@0: belugas@6560: static const byte _numof_industry_table[5][12] = { 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: { ludde@2072: int num = _numof_industry_table[_opt.diff.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: tron@2639: if (_opt.diff.number_industries != 0) { tron@2498: PlayerID old_player = _current_player; darkvater@266: _current_player = OWNER_NONE; orudge@61: assert(num > 0); truelight@0: truelight@0: do { tron@2639: uint i; tron@2639: truelight@4300: IncreaseGeneratingWorldProgress(GWP_INDUSTRY); truelight@4300: tron@2639: for (i = 0; i < 2000; i++) { tron@2639: if (CreateNewIndustry(RandomTile(), type) != NULL) break; tron@2639: } truelight@110: } while (--num); darkvater@266: darkvater@267: _current_player = old_player; orudge@61: } truelight@0: } truelight@0: belugas@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 */ belugas@6493: for (it = IT_COAL_MINE; it < NUM_INDUSTRYTYPES; it++) { truelight@4300: belugas@6442: ind_spc = GetIndustrySpec(it); belugas@6748: if (ind_spc->enabled) { belugas@6748: chance = ind_spc->appear_creation[_opt.landscape]; belugas@6748: if (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 */ belugas@6748: int num = _numof_industry_table[_opt.diff.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: belugas@6493: for (it = IT_COAL_MINE; it < NUM_INDUSTRYTYPES; it++) { belugas@6442: /* Once the number of industries has been determined, let's really create them. belugas@6442: * The test for chance allows us to try create industries that are available only belugas@6442: * for this landscape. belugas@6442: * @todo : Do we really have to pass chance as un-scaled value, since we've already belugas@6442: * processed that scaling above? No, don't think so. Will find a way. */ belugas@6748: ind_spc = GetIndustrySpec(it); belugas@6748: if (ind_spc->enabled) { belugas@6748: chance = ind_spc->appear_creation[_opt.landscape]; belugas@6748: if (chance > 0) PlaceInitialIndustry(it, chance); belugas@6748: } belugas@6442: }; truelight@0: } truelight@0: belugas@3496: /* Change industry production or do closure */ truelight@0: static void ExtChangeIndustryProduction(Industry *i) truelight@0: { truelight@1320: bool closeit = true; truelight@0: int j; belugas@3669: const IndustrySpec *indspec = GetIndustrySpec(i->type); truelight@0: belugas@4924: switch (indspec->life_type) { belugas@3496: case INDUSTRYLIFE_NOT_CLOSABLE: truelight@1320: return; truelight@201: belugas@3496: case INDUSTRYLIFE_CLOSABLE: rubidium@4329: if ((byte)(_cur_year - i->last_prod_year) < 5 || !CHANCE16(1, 180)) truelight@0: closeit = false; truelight@1320: break; truelight@201: truelight@1320: default: /* INDUSTRY_PRODUCTION */ belugas@6636: for (j = 0; j < 2 && indspec->produced_cargo[j] != CT_INVALID; j++){ pasky@1603: uint32 r = Random(); rubidium@5587: int old_prod, new_prod, percent; truelight@1320: int mag; truelight@1320: rubidium@5587: new_prod = old_prod = i->production_rate[j]; pasky@1603: if (CHANCE16I(20, 1024, r)) rubidium@5587: new_prod -= ((RandomRange(50) + 10) * old_prod) >> 8; rubidium@6819: if (CHANCE16I(20 + (i->last_month_pct_transported[j] * 20 >> 8), 1024, r >> 16)) rubidium@5587: new_prod += ((RandomRange(50) + 10) * old_prod) >> 8; truelight@1320: rubidium@5587: new_prod = clamp(new_prod, 0, 255); rubidium@5587: if (new_prod == old_prod) { truelight@1320: closeit = false; truelight@1320: continue; truelight@1320: } truelight@1320: rubidium@5587: percent = new_prod * 100 / old_prod - 100; rubidium@5587: i->production_rate[j] = new_prod; truelight@1320: rubidium@5587: if (new_prod >= indspec->production_rate[j] / 4) truelight@1320: closeit = false; truelight@1320: truelight@1320: mag = abs(percent); truelight@1320: if (mag >= 10) { ludde@2070: SetDParam(2, mag); belugas@6636: SetDParam(0, GetCargo(indspec->produced_cargo[j])->name); ludde@2070: SetDParam(1, i->index); tron@2639: AddNewsItem( tron@2639: percent >= 0 ? STR_INDUSTRY_PROD_GOUP : STR_INDUSTRY_PROD_GODOWN, rubidium@6491: NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, NT_ECONOMY, 0), tron@2639: i->xy + TileDiffXY(1, 1), 0 tron@2639: ); truelight@1320: } truelight@0: } truelight@1320: break; truelight@0: } truelight@0: belugas@3496: /* If industry will be closed down, show this */ truelight@0: if (closeit) { truelight@0: i->prod_level = 0; ludde@2070: SetDParam(0, i->index); tron@2639: AddNewsItem( belugas@3669: indspec->closure_text, rubidium@6491: NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, NT_OPENCLOSE, 0), tron@2639: i->xy + TileDiffXY(1, 1), 0 tron@2639: ); truelight@0: } truelight@0: } truelight@0: truelight@0: truelight@0: static void UpdateIndustryStatistics(Industry *i) truelight@0: { truelight@0: byte pct; belugas@6467: bool refresh = false; belugas@6636: const IndustrySpec *indsp = GetIndustrySpec(i->type); truelight@201: belugas@6636: for (byte j = 0; j < lengthof(indsp->produced_cargo); j++) { belugas@6636: if (indsp->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: belugas@6467: if (refresh) truelight@919: InvalidateWindow(WC_INDUSTRY_VIEW, i->index); truelight@0: tron@2639: if (i->prod_level == 0) { truelight@0: DeleteIndustry(i); tron@2639: } else if (_patches.smooth_economy) { truelight@0: ExtChangeIndustryProduction(i); tron@2639: } 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++) { belugas@6419: byte chance = GetIndustrySpec(j)->appear_ingame[_opt.landscape]; truelight@0: belugas@6419: /* if appearing chance for this landscape is above 0, this industry can be chosen */ belugas@6419: if (chance != 0) { 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); belugas@6419: SetDParam(1, ind->town->index); belugas@6266: AddNewsItem(ind_spc->new_industry_text, rubidium@6491: NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, NT_OPENCLOSE, 0), ind->xy, 0); truelight@0: } truelight@0: truelight@1320: static void ChangeIndustryProduction(Industry *i) truelight@0: { truelight@1320: bool only_decrease = false; truelight@1320: StringID str = STR_NULL; truelight@0: int type = i->type; belugas@3669: const IndustrySpec *indspec = GetIndustrySpec(type); truelight@201: belugas@4924: switch (indspec->life_type) { belugas@3496: case INDUSTRYLIFE_NOT_CLOSABLE: truelight@1320: return; truelight@0: belugas@3496: case INDUSTRYLIFE_PRODUCTION: truelight@1320: /* decrease or increase */ belugas@6390: if ((indspec->behaviour & INDUSTRYBEH_DONT_INCR_PROD) && _opt.landscape == LT_TEMPERATE) truelight@1320: only_decrease = true; truelight@201: rubidium@6491: if (only_decrease || CHANCE16(1, 3)) { truelight@1320: /* If you transport > 60%, 66% chance we increase, else 33% chance we increase */ rubidium@6819: if (!only_decrease && (i->last_month_pct_transported[0] > 153) != CHANCE16(1, 3)) { truelight@1320: /* Increase production */ truelight@1320: if (i->prod_level != 0x80) { truelight@1320: byte b; truelight@201: truelight@1320: i->prod_level <<= 1; truelight@1320: truelight@1320: b = i->production_rate[0] * 2; truelight@1320: if (i->production_rate[0] >= 128) truelight@1320: b = 0xFF; truelight@1320: i->production_rate[0] = b; truelight@1320: truelight@1320: b = i->production_rate[1] * 2; truelight@1320: if (i->production_rate[1] >= 128) truelight@1320: b = 0xFF; truelight@1320: i->production_rate[1] = b; truelight@1320: belugas@3669: str = indspec->production_up_text; truelight@1320: } truelight@1320: } else { truelight@1320: /* Decrease production */ truelight@1320: if (i->prod_level == 4) { truelight@1320: i->prod_level = 0; belugas@3669: str = indspec->closure_text; truelight@1320: } else { truelight@1320: i->prod_level >>= 1; truelight@1320: i->production_rate[0] = (i->production_rate[0] + 1) >> 1; truelight@1320: i->production_rate[1] = (i->production_rate[1] + 1) >> 1; truelight@1320: belugas@3669: str = indspec->production_down_text; truelight@1320: } truelight@0: } truelight@0: } truelight@1320: break; truelight@1320: belugas@3496: case INDUSTRYLIFE_CLOSABLE: truelight@1320: /* maybe close */ rubidium@6491: if ( (byte)(_cur_year - i->last_prod_year) >= 5 && CHANCE16(1, 2)) { truelight@1320: i->prod_level = 0; belugas@3669: str = indspec->closure_text; truelight@1320: } truelight@1320: break; truelight@1320: } truelight@1320: truelight@1320: if (str != STR_NULL) { ludde@2070: SetDParam(0, i->index); rubidium@6491: AddNewsItem(str, NEWS_FLAGS(NM_THIN, NF_VIEWPORT | NF_TILE, str == indspec->closure_text ? NT_OPENCLOSE : NT_ECONOMY, 0), i->xy + TileDiffXY(1, 1), 0); truelight@0: } truelight@0: } truelight@0: rubidium@6247: void IndustryMonthlyLoop() truelight@0: { truelight@0: Industry *i; tron@2498: PlayerID old_player = _current_player; darkvater@266: _current_player = OWNER_NONE; truelight@0: truelight@830: FOR_ALL_INDUSTRIES(i) { truelight@4346: UpdateIndustryStatistics(i); truelight@0: } truelight@0: truelight@1267: /* 3% chance that we start a new industry */ belugas@6433: if (CHANCE16(3, 100)) { belugas@6419: MaybeNewIndustry(); matthijs@5247: } else if (!_patches.smooth_economy) { truelight@4356: i = GetRandomIndustry(); truelight@4356: if (i != NULL) ChangeIndustryProduction(i); truelight@0: } darkvater@266: darkvater@266: _current_player = old_player; darkvater@266: belugas@6201: /* production-change */ truelight@0: _industry_sort_dirty = true; truelight@0: InvalidateWindow(WC_INDUSTRY_DIRECTORY, 0); truelight@0: } truelight@0: truelight@0: rubidium@6247: void InitializeIndustries() truelight@0: { tron@4976: CleanPool(&_Industry_pool); tron@4976: AddBlockToPool(&_Industry_pool); truelight@919: belugas@6839: ResetIndustryCounts(); truelight@0: _industry_sort_dirty = true; rubidium@4587: _industry_sound_tile = 0; truelight@0: } truelight@0: 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@4344: GetSlopeTileh_Industry, /* get_slope_tileh_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), belugas@6636: SLE_CONDNULL( 2, 2, 60), ///< used to be industry's produced_cargo rubidium@6819: SLE_ARR(Industry, cargo_waiting, SLE_UINT16, 2), rubidium@6819: SLE_ARR(Industry, production_rate, SLE_UINT8, 2), belugas@6636: SLE_CONDNULL( 3, 2, 60), ///< used to be industry's accepts_cargo 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: 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@6201: /* Write the vehicles */ truelight@830: FOR_ALL_INDUSTRIES(ind) { truelight@4346: SlSetArrayIndex(ind->index); truelight@4346: SlObject(ind, _industry_desc); truelight@0: } truelight@0: } truelight@0: 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) { truelight@1267: Industry *i; truelight@919: tron@4976: if (!AddBlockIfNeeded(&_Industry_pool, index)) truelight@1267: error("Industries: failed loading savegame: too many industries"); truelight@1267: truelight@1267: i = GetIndustry(index); truelight@919: SlObject(i, _industry_desc); belugas@6824: IncIndustryTypeCount(i->type); truelight@0: } truelight@0: } truelight@0: rubidium@5587: extern const ChunkHandler _industry_chunk_handlers[] = { truelight@0: { 'INDY', Save_INDY, Load_INDY, CH_ARRAY | CH_LAST}, truelight@0: };