00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "clear_map.h"
00014 #include "industry.h"
00015 #include "station_base.h"
00016 #include "train.h"
00017 #include "landscape.h"
00018 #include "viewport_func.h"
00019 #include "command_func.h"
00020 #include "town.h"
00021 #include "news_func.h"
00022 #include "variables.h"
00023 #include "cheat_type.h"
00024 #include "genworld.h"
00025 #include "tree_map.h"
00026 #include "newgrf.h"
00027 #include "newgrf_cargo.h"
00028 #include "newgrf_commons.h"
00029 #include "newgrf_industries.h"
00030 #include "newgrf_industrytiles.h"
00031 #include "autoslope.h"
00032 #include "water.h"
00033 #include "strings_func.h"
00034 #include "functions.h"
00035 #include "window_func.h"
00036 #include "date_func.h"
00037 #include "vehicle_func.h"
00038 #include "sound_func.h"
00039 #include "animated_tile_func.h"
00040 #include "effectvehicle_func.h"
00041 #include "effectvehicle_base.h"
00042 #include "ai/ai.hpp"
00043 #include "core/pool_func.hpp"
00044 #include "subsidy_func.h"
00045
00046 #include "table/strings.h"
00047 #include "table/industry_land.h"
00048 #include "table/build_industry.h"
00049
00050 IndustryPool _industry_pool("Industry");
00051 INSTANTIATE_POOL_METHODS(Industry)
00052
00053 void ShowIndustryViewWindow(int industry);
00054 void BuildOilRig(TileIndex tile);
00055
00056 static byte _industry_sound_ctr;
00057 static TileIndex _industry_sound_tile;
00058
00059 uint16 _industry_counts[NUM_INDUSTRYTYPES];
00060
00061 IndustrySpec _industry_specs[NUM_INDUSTRYTYPES];
00062 IndustryTileSpec _industry_tile_specs[NUM_INDUSTRYTILES];
00063
00068 void ResetIndustries()
00069 {
00070 memset(&_industry_specs, 0, sizeof(_industry_specs));
00071 memcpy(&_industry_specs, &_origin_industry_specs, sizeof(_origin_industry_specs));
00072
00073
00074 for (IndustryType i = 0; i < NUM_INDUSTRYTYPES; i++) {
00075 _industry_specs[i].enabled = i < NEW_INDUSTRYOFFSET &&
00076 HasBit(_origin_industry_specs[i].climate_availability, _settings_game.game_creation.landscape);
00077 }
00078
00079 memset(&_industry_tile_specs, 0, sizeof(_industry_tile_specs));
00080 memcpy(&_industry_tile_specs, &_origin_industry_tile_specs, sizeof(_origin_industry_tile_specs));
00081
00082
00083 _industile_mngr.ResetOverride();
00084 _industry_mngr.ResetOverride();
00085 }
00086
00087 void ResetIndustryCreationProbility(IndustryType type)
00088 {
00089 assert(type < INVALID_INDUSTRYTYPE);
00090 _industry_specs[type].appear_creation[_settings_game.game_creation.landscape] = 0;
00091 }
00092
00101 IndustryType GetIndustryType(TileIndex tile)
00102 {
00103 assert(IsTileType(tile, MP_INDUSTRY));
00104
00105 const Industry *ind = Industry::GetByTile(tile);
00106 assert(ind != NULL);
00107 return ind->type;
00108 }
00109
00118 const IndustrySpec *GetIndustrySpec(IndustryType thistype)
00119 {
00120 assert(thistype < NUM_INDUSTRYTYPES);
00121 return &_industry_specs[thistype];
00122 }
00123
00132 const IndustryTileSpec *GetIndustryTileSpec(IndustryGfx gfx)
00133 {
00134 assert(gfx < INVALID_INDUSTRYTILE);
00135 return &_industry_tile_specs[gfx];
00136 }
00137
00138 Industry::~Industry()
00139 {
00140 if (CleaningPool()) return;
00141
00142
00143
00144 if (this->location.w == 0) return;
00145
00146 TILE_AREA_LOOP(tile_cur, this->location) {
00147 if (IsTileType(tile_cur, MP_INDUSTRY)) {
00148 if (GetIndustryIndex(tile_cur) == this->index) {
00149
00150 MakeWaterKeepingClass(tile_cur, OWNER_NONE);
00151
00152
00153
00154 DeleteAnimatedTile(tile_cur);
00155
00156 MarkTileDirtyByTile(tile_cur);
00157 }
00158 } else if (IsTileType(tile_cur, MP_STATION) && IsOilRig(tile_cur)) {
00159 DeleteOilRig(tile_cur);
00160 }
00161 }
00162
00163 if (GetIndustrySpec(this->type)->behaviour & INDUSTRYBEH_PLANT_FIELDS) {
00164
00165 TILE_LOOP(tile_cur, 42, 42, this->location.tile - TileDiffXY(21, 21)) {
00166 tile_cur = TILE_MASK(tile_cur);
00167 if (IsTileType(tile_cur, MP_CLEAR) && IsClearGround(tile_cur, CLEAR_FIELDS) &&
00168 GetIndustryIndexOfField(tile_cur) == this->index) {
00169 SetIndustryIndexOfField(tile_cur, INVALID_INDUSTRY);
00170 }
00171 }
00172 }
00173
00174
00175 ReleaseDisastersTargetingIndustry(this->index);
00176
00177 DecIndustryTypeCount(this->type);
00178
00179 DeleteIndustryNews(this->index);
00180 DeleteWindowById(WC_INDUSTRY_VIEW, this->index);
00181
00182 DeleteSubsidyWith(ST_INDUSTRY, this->index);
00183 CargoPacket::InvalidateAllFrom(ST_INDUSTRY, this->index);
00184 }
00185
00190 void Industry::PostDestructor(size_t index)
00191 {
00192 InvalidateWindowData(WC_INDUSTRY_DIRECTORY, 0, 0);
00193 Station::RecomputeIndustriesNearForAll();
00194 }
00195
00196
00201 Industry *Industry::GetRandom()
00202 {
00203 if (Industry::GetNumItems() == 0) return NULL;
00204 int num = RandomRange((uint16)Industry::GetNumItems());
00205 size_t index = MAX_UVALUE(size_t);
00206
00207 while (num >= 0) {
00208 num--;
00209 index++;
00210
00211
00212 while (!Industry::IsValidID(index)) {
00213 index++;
00214 assert(index < Industry::GetPoolSize());
00215 }
00216 }
00217
00218 return Industry::Get(index);
00219 }
00220
00221
00222 static void IndustryDrawSugarMine(const TileInfo *ti)
00223 {
00224 const DrawIndustryAnimationStruct *d;
00225
00226 if (!IsIndustryCompleted(ti->tile)) return;
00227
00228 d = &_draw_industry_spec1[GetIndustryAnimationState(ti->tile)];
00229
00230 AddChildSpriteScreen(SPR_IT_SUGAR_MINE_SIEVE + d->image_1, PAL_NONE, d->x, 0);
00231
00232 if (d->image_2 != 0) {
00233 AddChildSpriteScreen(SPR_IT_SUGAR_MINE_CLOUDS + d->image_2 - 1, PAL_NONE, 8, 41);
00234 }
00235
00236 if (d->image_3 != 0) {
00237 AddChildSpriteScreen(SPR_IT_SUGAR_MINE_PILE + d->image_3 - 1, PAL_NONE,
00238 _drawtile_proc1[d->image_3 - 1].x, _drawtile_proc1[d->image_3 - 1].y);
00239 }
00240 }
00241
00242 static void IndustryDrawToffeeQuarry(const TileInfo *ti)
00243 {
00244 uint8 x = 0;
00245
00246 if (IsIndustryCompleted(ti->tile)) {
00247 x = _industry_anim_offs_toffee[GetIndustryAnimationState(ti->tile)];
00248 if (x == 0xFF)
00249 x = 0;
00250 }
00251
00252 AddChildSpriteScreen(SPR_IT_TOFFEE_QUARRY_SHOVEL, PAL_NONE, 22 - x, 24 + x);
00253 AddChildSpriteScreen(SPR_IT_TOFFEE_QUARRY_TOFFEE, PAL_NONE, 6, 14);
00254 }
00255
00256 static void IndustryDrawBubbleGenerator( const TileInfo *ti)
00257 {
00258 if (IsIndustryCompleted(ti->tile)) {
00259 AddChildSpriteScreen(SPR_IT_BUBBLE_GENERATOR_BUBBLE, PAL_NONE, 5, _industry_anim_offs_bubbles[GetIndustryAnimationState(ti->tile)]);
00260 } else {
00261 AddChildSpriteScreen(SPR_IT_BUBBLE_GENERATOR_SPRING, PAL_NONE, 3, 67);
00262 }
00263 }
00264
00265 static void IndustryDrawToyFactory(const TileInfo *ti)
00266 {
00267 const DrawIndustryAnimationStruct *d;
00268
00269 d = &_industry_anim_offs_toys[GetIndustryAnimationState(ti->tile)];
00270
00271 if (d->image_1 != 0xFF) {
00272 AddChildSpriteScreen(SPR_IT_TOY_FACTORY_CLAY, PAL_NONE, d->x, 96 + d->image_1);
00273 }
00274
00275 if (d->image_2 != 0xFF) {
00276 AddChildSpriteScreen(SPR_IT_TOY_FACTORY_ROBOT, PAL_NONE, 16 - d->image_2 * 2, 100 + d->image_2);
00277 }
00278
00279 AddChildSpriteScreen(SPR_IT_TOY_FACTORY_STAMP, PAL_NONE, 7, d->image_3);
00280 AddChildSpriteScreen(SPR_IT_TOY_FACTORY_STAMP_HOLDER, PAL_NONE, 0, 42);
00281 }
00282
00283 static void IndustryDrawCoalPlantSparks(const TileInfo *ti)
00284 {
00285 if (IsIndustryCompleted(ti->tile)) {
00286 uint8 image = GetIndustryAnimationState(ti->tile);
00287
00288 if (image != 0 && image < 7) {
00289 AddChildSpriteScreen(image + SPR_IT_POWER_PLANT_TRANSFORMERS,
00290 PAL_NONE,
00291 _coal_plant_sparks[image - 1].x,
00292 _coal_plant_sparks[image - 1].y
00293 );
00294 }
00295 }
00296 }
00297
00298 typedef void IndustryDrawTileProc(const TileInfo *ti);
00299 static IndustryDrawTileProc * const _industry_draw_tile_procs[5] = {
00300 IndustryDrawSugarMine,
00301 IndustryDrawToffeeQuarry,
00302 IndustryDrawBubbleGenerator,
00303 IndustryDrawToyFactory,
00304 IndustryDrawCoalPlantSparks,
00305 };
00306
00307 static void DrawTile_Industry(TileInfo *ti)
00308 {
00309 IndustryGfx gfx = GetIndustryGfx(ti->tile);
00310 Industry *ind = Industry::GetByTile(ti->tile);
00311 const IndustryTileSpec *indts = GetIndustryTileSpec(gfx);
00312 const DrawBuildingsTileStruct *dits;
00313
00314
00315 if (gfx >= NEW_INDUSTRYTILEOFFSET) {
00316
00317
00318
00319
00320 if (indts->grf_prop.spritegroup != NULL && DrawNewIndustryTile(ti, ind, gfx, indts)) {
00321 return;
00322 } else {
00323
00324
00325 if (indts->grf_prop.subst_id != INVALID_INDUSTRYTILE) {
00326 gfx = indts->grf_prop.subst_id;
00327
00328 indts = GetIndustryTileSpec(gfx);
00329 }
00330 }
00331 }
00332
00333 dits = &_industry_draw_tile_data[gfx << 2 | (indts->anim_state ?
00334 GetIndustryAnimationState(ti->tile) & INDUSTRY_COMPLETED :
00335 GetIndustryConstructionStage(ti->tile))];
00336
00337 SpriteID image = dits->ground.sprite;
00338
00339
00340 if (ti->tileh != SLOPE_FLAT) DrawFoundation(ti, FOUNDATION_LEVELED);
00341
00342
00343
00344 if (image == SPR_FLAT_WATER_TILE && IsIndustryTileOnWater(ti->tile)) {
00345 DrawWaterClassGround(ti);
00346 } else {
00347 DrawGroundSprite(image, GroundSpritePaletteTransform(image, dits->ground.pal, GENERAL_SPRITE_COLOUR(ind->random_colour)));
00348 }
00349
00350
00351 if (IsInvisibilitySet(TO_INDUSTRIES)) return;
00352
00353
00354 image = dits->building.sprite;
00355 if (image != 0) {
00356 AddSortableSpriteToDraw(image, SpriteLayoutPaletteTransform(image, dits->building.pal, GENERAL_SPRITE_COLOUR(ind->random_colour)),
00357 ti->x + dits->subtile_x,
00358 ti->y + dits->subtile_y,
00359 dits->width,
00360 dits->height,
00361 dits->dz,
00362 ti->z,
00363 IsTransparencySet(TO_INDUSTRIES));
00364
00365 if (IsTransparencySet(TO_INDUSTRIES)) return;
00366 }
00367
00368 {
00369 int proc = dits->draw_proc - 1;
00370 if (proc >= 0) _industry_draw_tile_procs[proc](ti);
00371 }
00372 }
00373
00374 static uint GetSlopeZ_Industry(TileIndex tile, uint x, uint y)
00375 {
00376 return GetTileMaxZ(tile);
00377 }
00378
00379 static Foundation GetFoundation_Industry(TileIndex tile, Slope tileh)
00380 {
00381 IndustryGfx gfx = GetIndustryGfx(tile);
00382
00383
00384
00385
00386
00387 if (gfx >= NEW_INDUSTRYTILEOFFSET) {
00388 const IndustryTileSpec *indts = GetIndustryTileSpec(gfx);
00389 if (indts->grf_prop.spritegroup != NULL && HasBit(indts->callback_mask, CBM_INDT_DRAW_FOUNDATIONS)) {
00390 uint32 callback_res = GetIndustryTileCallback(CBID_INDUSTRY_DRAW_FOUNDATIONS, 0, 0, gfx, Industry::GetByTile(tile), tile);
00391 if (callback_res == 0) return FOUNDATION_NONE;
00392 }
00393 }
00394 return FlatteningFoundation(tileh);
00395 }
00396
00397 static void AddAcceptedCargo_Industry(TileIndex tile, CargoArray &acceptance, uint32 *always_accepted)
00398 {
00399 IndustryGfx gfx = GetIndustryGfx(tile);
00400 const IndustryTileSpec *itspec = GetIndustryTileSpec(gfx);
00401
00402
00403 CargoID raw_accepts_cargo[lengthof(itspec->accepts_cargo)];
00404 uint8 raw_cargo_acceptance[lengthof(itspec->acceptance)];
00405
00406
00407 const CargoID *accepts_cargo = itspec->accepts_cargo;
00408 const uint8 *cargo_acceptance = itspec->acceptance;
00409
00410 if (HasBit(itspec->callback_mask, CBM_INDT_ACCEPT_CARGO)) {
00411 uint16 res = GetIndustryTileCallback(CBID_INDTILE_ACCEPT_CARGO, 0, 0, gfx, Industry::GetByTile(tile), tile);
00412 if (res != CALLBACK_FAILED) {
00413 accepts_cargo = raw_accepts_cargo;
00414 for (uint i = 0; i < lengthof(itspec->accepts_cargo); i++) raw_accepts_cargo[i] = GetCargoTranslation(GB(res, i * 5, 5), itspec->grf_prop.grffile);
00415 }
00416 }
00417
00418 if (HasBit(itspec->callback_mask, CBM_INDT_CARGO_ACCEPTANCE)) {
00419 uint16 res = GetIndustryTileCallback(CBID_INDTILE_CARGO_ACCEPTANCE, 0, 0, gfx, Industry::GetByTile(tile), tile);
00420 if (res != CALLBACK_FAILED) {
00421 cargo_acceptance = raw_cargo_acceptance;
00422 for (uint i = 0; i < lengthof(itspec->accepts_cargo); i++) raw_cargo_acceptance[i] = GB(res, i * 4, 4);
00423 }
00424 }
00425
00426 const Industry *ind = Industry::GetByTile(tile);
00427 for (byte i = 0; i < lengthof(itspec->accepts_cargo); i++) {
00428 CargoID a = accepts_cargo[i];
00429 if (a == CT_INVALID || cargo_acceptance[i] == 0) continue;
00430
00431
00432 acceptance[a] += cargo_acceptance[i];
00433
00434
00435 if (HasBit(*always_accepted, a)) continue;
00436
00437 bool accepts = false;
00438 for (uint cargo_index = 0; cargo_index < lengthof(ind->accepts_cargo); cargo_index++) {
00439
00440 if (ind->accepts_cargo[cargo_index] == a) {
00441 accepts = true;
00442 break;
00443 }
00444 }
00445
00446 if (accepts) continue;
00447
00448
00449 SetBit(*always_accepted, a);
00450 }
00451 }
00452
00453 static void GetTileDesc_Industry(TileIndex tile, TileDesc *td)
00454 {
00455 const Industry *i = Industry::GetByTile(tile);
00456 const IndustrySpec *is = GetIndustrySpec(i->type);
00457
00458 td->owner[0] = i->owner;
00459 td->str = is->name;
00460 if (!IsIndustryCompleted(tile)) {
00461 SetDParamX(td->dparam, 0, td->str);
00462 td->str = STR_LAI_TOWN_INDUSTRY_DESCRIPTION_UNDER_CONSTRUCTION;
00463 }
00464
00465 if (is->grf_prop.grffile != NULL) {
00466 td->grf = GetGRFConfig(is->grf_prop.grffile->grfid)->name;
00467 }
00468 }
00469
00470 static CommandCost ClearTile_Industry(TileIndex tile, DoCommandFlag flags)
00471 {
00472 Industry *i = Industry::GetByTile(tile);
00473 const IndustrySpec *indspec = GetIndustrySpec(i->type);
00474
00475
00476
00477
00478
00479
00480 if ((_current_company != OWNER_WATER && _game_mode != GM_EDITOR &&
00481 !_cheats.magic_bulldozer.value) ||
00482 ((flags & DC_AUTO) != 0) ||
00483 (_current_company == OWNER_WATER &&
00484 ((indspec->behaviour & INDUSTRYBEH_BUILT_ONWATER) ||
00485 HasBit(GetIndustryTileSpec(GetIndustryGfx(tile))->slopes_refused, 5)))) {
00486 SetDParam(0, indspec->name);
00487 return_cmd_error(flags & DC_AUTO ? STR_ERROR_UNMOVABLE_OBJECT_IN_THE_WAY : INVALID_STRING_ID);
00488 }
00489
00490 if (flags & DC_EXEC) {
00491 AI::BroadcastNewEvent(new AIEventIndustryClose(i->index));
00492 delete i;
00493 }
00494 return CommandCost(EXPENSES_CONSTRUCTION, indspec->GetRemovalCost());
00495 }
00496
00497 static void TransportIndustryGoods(TileIndex tile)
00498 {
00499 Industry *i = Industry::GetByTile(tile);
00500 const IndustrySpec *indspec = GetIndustrySpec(i->type);
00501 bool moved_cargo = false;
00502
00503 StationFinder stations(i->location);
00504
00505 for (uint j = 0; j < lengthof(i->produced_cargo_waiting); j++) {
00506 uint cw = min(i->produced_cargo_waiting[j], 255);
00507 if (cw > indspec->minimal_cargo && i->produced_cargo[j] != CT_INVALID) {
00508 i->produced_cargo_waiting[j] -= cw;
00509
00510
00511 if (_economy.fluct <= 0) cw = (cw + 1) / 2;
00512
00513 i->this_month_production[j] += cw;
00514
00515 uint am = MoveGoodsToStation(i->produced_cargo[j], cw, ST_INDUSTRY, i->index, stations.GetStations());
00516 i->this_month_transported[j] += am;
00517
00518 moved_cargo |= (am != 0);
00519 }
00520 }
00521
00522 if (moved_cargo && !StartStopIndustryTileAnimation(i, IAT_INDUSTRY_DISTRIBUTES_CARGO)) {
00523 uint newgfx = GetIndustryTileSpec(GetIndustryGfx(tile))->anim_production;
00524
00525 if (newgfx != INDUSTRYTILE_NOANIM) {
00526 ResetIndustryConstructionStage(tile);
00527 SetIndustryCompleted(tile, true);
00528 SetIndustryGfx(tile, newgfx);
00529 MarkTileDirtyByTile(tile);
00530 }
00531 }
00532 }
00533
00534
00535 static void AnimateTile_Industry(TileIndex tile)
00536 {
00537 byte m;
00538 IndustryGfx gfx = GetIndustryGfx(tile);
00539
00540 if (GetIndustryTileSpec(gfx)->animation_info != 0xFFFF) {
00541 AnimateNewIndustryTile(tile);
00542 return;
00543 }
00544
00545 switch (gfx) {
00546 case GFX_SUGAR_MINE_SIEVE:
00547 if ((_tick_counter & 1) == 0) {
00548 m = GetIndustryAnimationState(tile) + 1;
00549
00550 switch (m & 7) {
00551 case 2: SndPlayTileFx(SND_2D_RIP_2, tile); break;
00552 case 6: SndPlayTileFx(SND_29_RIP, tile); break;
00553 }
00554
00555 if (m >= 96) {
00556 m = 0;
00557 DeleteAnimatedTile(tile);
00558 }
00559 SetIndustryAnimationState(tile, m);
00560
00561 MarkTileDirtyByTile(tile);
00562 }
00563 break;
00564
00565 case GFX_TOFFEE_QUARY:
00566 if ((_tick_counter & 3) == 0) {
00567 m = GetIndustryAnimationState(tile);
00568
00569 if (_industry_anim_offs_toffee[m] == 0xFF) {
00570 SndPlayTileFx(SND_30_CARTOON_SOUND, tile);
00571 }
00572
00573 if (++m >= 70) {
00574 m = 0;
00575 DeleteAnimatedTile(tile);
00576 }
00577 SetIndustryAnimationState(tile, m);
00578
00579 MarkTileDirtyByTile(tile);
00580 }
00581 break;
00582
00583 case GFX_BUBBLE_CATCHER:
00584 if ((_tick_counter & 1) == 0) {
00585 m = GetIndustryAnimationState(tile);
00586
00587 if (++m >= 40) {
00588 m = 0;
00589 DeleteAnimatedTile(tile);
00590 }
00591 SetIndustryAnimationState(tile, m);
00592
00593 MarkTileDirtyByTile(tile);
00594 }
00595 break;
00596
00597
00598 case GFX_POWERPLANT_SPARKS:
00599 if ((_tick_counter & 3) == 0) {
00600 m = GetIndustryAnimationState(tile);
00601 if (m == 6) {
00602 SetIndustryAnimationState(tile, 0);
00603 DeleteAnimatedTile(tile);
00604 } else {
00605 SetIndustryAnimationState(tile, m + 1);
00606 MarkTileDirtyByTile(tile);
00607 }
00608 }
00609 break;
00610
00611 case GFX_TOY_FACTORY:
00612 if ((_tick_counter & 1) == 0) {
00613 m = GetIndustryAnimationState(tile) + 1;
00614
00615 switch (m) {
00616 case 1: SndPlayTileFx(SND_2C_MACHINERY, tile); break;
00617 case 23: SndPlayTileFx(SND_2B_COMEDY_HIT, tile); break;
00618 case 28: SndPlayTileFx(SND_2A_EXTRACT_AND_POP, tile); break;
00619 default:
00620 if (m >= 50) {
00621 int n = GetIndustryAnimationLoop(tile) + 1;
00622 m = 0;
00623 if (n >= 8) {
00624 n = 0;
00625 DeleteAnimatedTile(tile);
00626 }
00627 SetIndustryAnimationLoop(tile, n);
00628 }
00629 }
00630
00631 SetIndustryAnimationState(tile, m);
00632 MarkTileDirtyByTile(tile);
00633 }
00634 break;
00635
00636 case GFX_PLASTIC_FOUNTAIN_ANIMATED_1: case GFX_PLASTIC_FOUNTAIN_ANIMATED_2:
00637 case GFX_PLASTIC_FOUNTAIN_ANIMATED_3: case GFX_PLASTIC_FOUNTAIN_ANIMATED_4:
00638 case GFX_PLASTIC_FOUNTAIN_ANIMATED_5: case GFX_PLASTIC_FOUNTAIN_ANIMATED_6:
00639 case GFX_PLASTIC_FOUNTAIN_ANIMATED_7: case GFX_PLASTIC_FOUNTAIN_ANIMATED_8:
00640 if ((_tick_counter & 3) == 0) {
00641 IndustryGfx gfx = GetIndustryGfx(tile);
00642
00643 gfx = (gfx < 155) ? gfx + 1 : 148;
00644 SetIndustryGfx(tile, gfx);
00645 MarkTileDirtyByTile(tile);
00646 }
00647 break;
00648
00649 case GFX_OILWELL_ANIMATED_1:
00650 case GFX_OILWELL_ANIMATED_2:
00651 case GFX_OILWELL_ANIMATED_3:
00652 if ((_tick_counter & 7) == 0) {
00653 bool b = Chance16(1, 7);
00654 IndustryGfx gfx = GetIndustryGfx(tile);
00655
00656 m = GetIndustryAnimationState(tile) + 1;
00657 if (m == 4 && (m = 0, ++gfx) == GFX_OILWELL_ANIMATED_3 + 1 && (gfx = GFX_OILWELL_ANIMATED_1, b)) {
00658 SetIndustryGfx(tile, GFX_OILWELL_NOT_ANIMATED);
00659 SetIndustryConstructionStage(tile, 3);
00660 DeleteAnimatedTile(tile);
00661 } else {
00662 SetIndustryAnimationState(tile, m);
00663 SetIndustryGfx(tile, gfx);
00664 MarkTileDirtyByTile(tile);
00665 }
00666 }
00667 break;
00668
00669 case GFX_COAL_MINE_TOWER_ANIMATED:
00670 case GFX_COPPER_MINE_TOWER_ANIMATED:
00671 case GFX_GOLD_MINE_TOWER_ANIMATED: {
00672 int state = _tick_counter & 0x7FF;
00673
00674 if ((state -= 0x400) < 0)
00675 return;
00676
00677 if (state < 0x1A0) {
00678 if (state < 0x20 || state >= 0x180) {
00679 m = GetIndustryAnimationState(tile);
00680 if (!(m & 0x40)) {
00681 SetIndustryAnimationState(tile, m | 0x40);
00682 SndPlayTileFx(SND_0B_MINING_MACHINERY, tile);
00683 }
00684 if (state & 7)
00685 return;
00686 } else {
00687 if (state & 3)
00688 return;
00689 }
00690 m = (GetIndustryAnimationState(tile) + 1) | 0x40;
00691 if (m > 0xC2) m = 0xC0;
00692 SetIndustryAnimationState(tile, m);
00693 MarkTileDirtyByTile(tile);
00694 } else if (state >= 0x200 && state < 0x3A0) {
00695 int i;
00696 i = (state < 0x220 || state >= 0x380) ? 7 : 3;
00697 if (state & i)
00698 return;
00699
00700 m = (GetIndustryAnimationState(tile) & 0xBF) - 1;
00701 if (m < 0x80) m = 0x82;
00702 SetIndustryAnimationState(tile, m);
00703 MarkTileDirtyByTile(tile);
00704 }
00705 } break;
00706 }
00707 }
00708
00709 static void CreateChimneySmoke(TileIndex tile)
00710 {
00711 uint x = TileX(tile) * TILE_SIZE;
00712 uint y = TileY(tile) * TILE_SIZE;
00713 uint z = GetTileMaxZ(tile);
00714
00715 CreateEffectVehicle(x + 15, y + 14, z + 59, EV_CHIMNEY_SMOKE);
00716 }
00717
00718 static void MakeIndustryTileBigger(TileIndex tile)
00719 {
00720 byte cnt = GetIndustryConstructionCounter(tile) + 1;
00721 byte stage;
00722
00723 if (cnt != 4) {
00724 SetIndustryConstructionCounter(tile, cnt);
00725 return;
00726 }
00727
00728 stage = GetIndustryConstructionStage(tile) + 1;
00729 SetIndustryConstructionCounter(tile, 0);
00730 SetIndustryConstructionStage(tile, stage);
00731 StartStopIndustryTileAnimation(tile, IAT_CONSTRUCTION_STATE_CHANGE);
00732 if (stage == INDUSTRY_COMPLETED) SetIndustryCompleted(tile, true);
00733
00734 MarkTileDirtyByTile(tile);
00735
00736 if (!IsIndustryCompleted(tile)) return;
00737
00738 IndustryGfx gfx = GetIndustryGfx(tile);
00739 if (gfx >= NEW_INDUSTRYTILEOFFSET) {
00740
00741 return;
00742 }
00743
00744 switch (gfx) {
00745 case GFX_POWERPLANT_CHIMNEY:
00746 CreateChimneySmoke(tile);
00747 break;
00748
00749 case GFX_OILRIG_1: {
00750
00751
00752
00753
00754 TileIndex other = tile + TileDiffXY(0, 1);
00755
00756 if (IsTileType(other, MP_INDUSTRY) &&
00757 GetIndustryGfx(other) == GFX_OILRIG_1 &&
00758 GetIndustryIndex(tile) == GetIndustryIndex(other)) {
00759 BuildOilRig(tile);
00760 }
00761 } break;
00762
00763 case GFX_TOY_FACTORY:
00764 case GFX_BUBBLE_CATCHER:
00765 case GFX_TOFFEE_QUARY:
00766 SetIndustryAnimationState(tile, 0);
00767 SetIndustryAnimationLoop(tile, 0);
00768 break;
00769
00770 case GFX_PLASTIC_FOUNTAIN_ANIMATED_1: case GFX_PLASTIC_FOUNTAIN_ANIMATED_2:
00771 case GFX_PLASTIC_FOUNTAIN_ANIMATED_3: case GFX_PLASTIC_FOUNTAIN_ANIMATED_4:
00772 case GFX_PLASTIC_FOUNTAIN_ANIMATED_5: case GFX_PLASTIC_FOUNTAIN_ANIMATED_6:
00773 case GFX_PLASTIC_FOUNTAIN_ANIMATED_7: case GFX_PLASTIC_FOUNTAIN_ANIMATED_8:
00774 AddAnimatedTile(tile);
00775 break;
00776 }
00777 }
00778
00779 static void TileLoopIndustry_BubbleGenerator(TileIndex tile)
00780 {
00781 static const int8 _bubble_spawn_location[3][4] = {
00782 { 11, 0, -4, -14 },
00783 { -4, -10, -4, 1 },
00784 { 49, 59, 60, 65 },
00785 };
00786
00787 SndPlayTileFx(SND_2E_EXTRACT_AND_POP, tile);
00788
00789 int dir = Random() & 3;
00790
00791 EffectVehicle *v = CreateEffectVehicleAbove(
00792 TileX(tile) * TILE_SIZE + _bubble_spawn_location[0][dir],
00793 TileY(tile) * TILE_SIZE + _bubble_spawn_location[1][dir],
00794 _bubble_spawn_location[2][dir],
00795 EV_BUBBLE
00796 );
00797
00798 if (v != NULL) v->animation_substate = dir;
00799 }
00800
00801 static void TileLoop_Industry(TileIndex tile)
00802 {
00803 IndustryGfx newgfx;
00804 IndustryGfx gfx;
00805
00806 if (IsIndustryTileOnWater(tile)) TileLoop_Water(tile);
00807
00808 TriggerIndustryTile(tile, INDTILE_TRIGGER_TILE_LOOP);
00809
00810 if (!IsIndustryCompleted(tile)) {
00811 MakeIndustryTileBigger(tile);
00812 return;
00813 }
00814
00815 if (_game_mode == GM_EDITOR) return;
00816
00817 TransportIndustryGoods(tile);
00818
00819 if (StartStopIndustryTileAnimation(tile, IAT_TILELOOP)) return;
00820
00821 newgfx = GetIndustryTileSpec(GetIndustryGfx(tile))->anim_next;
00822 if (newgfx != INDUSTRYTILE_NOANIM) {
00823 ResetIndustryConstructionStage(tile);
00824 SetIndustryGfx(tile, newgfx);
00825 MarkTileDirtyByTile(tile);
00826 return;
00827 }
00828
00829 gfx = GetIndustryGfx(tile);
00830
00831 switch (gfx) {
00832 case GFX_COAL_MINE_TOWER_NOT_ANIMATED:
00833 case GFX_COPPER_MINE_TOWER_NOT_ANIMATED:
00834 case GFX_GOLD_MINE_TOWER_NOT_ANIMATED:
00835 if (!(_tick_counter & 0x400) && Chance16(1, 2)) {
00836 switch (gfx) {
00837 case GFX_COAL_MINE_TOWER_NOT_ANIMATED: gfx = GFX_COAL_MINE_TOWER_ANIMATED; break;
00838 case GFX_COPPER_MINE_TOWER_NOT_ANIMATED: gfx = GFX_COPPER_MINE_TOWER_ANIMATED; break;
00839 case GFX_GOLD_MINE_TOWER_NOT_ANIMATED: gfx = GFX_GOLD_MINE_TOWER_ANIMATED; break;
00840 }
00841 SetIndustryGfx(tile, gfx);
00842 SetIndustryAnimationState(tile, 0x80);
00843 AddAnimatedTile(tile);
00844 }
00845 break;
00846
00847 case GFX_OILWELL_NOT_ANIMATED:
00848 if (Chance16(1, 6)) {
00849 SetIndustryGfx(tile, GFX_OILWELL_ANIMATED_1);
00850 SetIndustryAnimationState(tile, 0);
00851 AddAnimatedTile(tile);
00852 }
00853 break;
00854
00855 case GFX_COAL_MINE_TOWER_ANIMATED:
00856 case GFX_COPPER_MINE_TOWER_ANIMATED:
00857 case GFX_GOLD_MINE_TOWER_ANIMATED:
00858 if (!(_tick_counter & 0x400)) {
00859 switch (gfx) {
00860 case GFX_COAL_MINE_TOWER_ANIMATED: gfx = GFX_COAL_MINE_TOWER_NOT_ANIMATED; break;
00861 case GFX_COPPER_MINE_TOWER_ANIMATED: gfx = GFX_COPPER_MINE_TOWER_NOT_ANIMATED; break;
00862 case GFX_GOLD_MINE_TOWER_ANIMATED: gfx = GFX_GOLD_MINE_TOWER_NOT_ANIMATED; break;
00863 }
00864 SetIndustryGfx(tile, gfx);
00865 SetIndustryCompleted(tile, true);
00866 SetIndustryConstructionStage(tile, 3);
00867 DeleteAnimatedTile(tile);
00868 }
00869 break;
00870
00871 case GFX_POWERPLANT_SPARKS:
00872 if (Chance16(1, 3)) {
00873 SndPlayTileFx(SND_0C_ELECTRIC_SPARK, tile);
00874 AddAnimatedTile(tile);
00875 }
00876 break;
00877
00878 case GFX_COPPER_MINE_CHIMNEY:
00879 CreateEffectVehicleAbove(TileX(tile) * TILE_SIZE + 6, TileY(tile) * TILE_SIZE + 6, 43, EV_SMOKE);
00880 break;
00881
00882
00883 case GFX_TOY_FACTORY: {
00884 Industry *i = Industry::GetByTile(tile);
00885 if (i->was_cargo_delivered) {
00886 i->was_cargo_delivered = false;
00887 SetIndustryAnimationLoop(tile, 0);
00888 AddAnimatedTile(tile);
00889 }
00890 }
00891 break;
00892
00893 case GFX_BUBBLE_GENERATOR:
00894 TileLoopIndustry_BubbleGenerator(tile);
00895 break;
00896
00897 case GFX_TOFFEE_QUARY:
00898 AddAnimatedTile(tile);
00899 break;
00900
00901 case GFX_SUGAR_MINE_SIEVE:
00902 if (Chance16(1, 3)) AddAnimatedTile(tile);
00903 break;
00904 }
00905 }
00906
00907 static bool ClickTile_Industry(TileIndex tile)
00908 {
00909 ShowIndustryViewWindow(GetIndustryIndex(tile));
00910 return true;
00911 }
00912
00913 static TrackStatus GetTileTrackStatus_Industry(TileIndex tile, TransportType mode, uint sub_mode, DiagDirection side)
00914 {
00915 return 0;
00916 }
00917
00918 static void ChangeTileOwner_Industry(TileIndex tile, Owner old_owner, Owner new_owner)
00919 {
00920
00921 Industry *i = Industry::GetByTile(tile);
00922 if (i->founder == old_owner) i->founder = (new_owner == INVALID_OWNER) ? OWNER_NONE : new_owner;
00923 }
00924
00925 static const byte _plantfarmfield_type[] = {1, 1, 1, 1, 1, 3, 3, 4, 4, 4, 5, 5, 5, 6, 6, 6};
00926
00927 static bool IsBadFarmFieldTile(TileIndex tile)
00928 {
00929 switch (GetTileType(tile)) {
00930 case MP_CLEAR: return IsClearGround(tile, CLEAR_FIELDS) || IsClearGround(tile, CLEAR_SNOW) || IsClearGround(tile, CLEAR_DESERT);
00931 case MP_TREES: return (GetTreeGround(tile) == TREE_GROUND_SHORE);
00932 default: return true;
00933 }
00934 }
00935
00936 static bool IsBadFarmFieldTile2(TileIndex tile)
00937 {
00938 switch (GetTileType(tile)) {
00939 case MP_CLEAR: return IsClearGround(tile, CLEAR_SNOW) || IsClearGround(tile, CLEAR_DESERT);
00940 case MP_TREES: return (GetTreeGround(tile) == TREE_GROUND_SHORE);
00941 default: return true;
00942 }
00943 }
00944
00945 static void SetupFarmFieldFence(TileIndex tile, int size, byte type, Axis direction)
00946 {
00947 do {
00948 tile = TILE_MASK(tile);
00949
00950 if (IsTileType(tile, MP_CLEAR) || IsTileType(tile, MP_TREES)) {
00951 byte or_ = type;
00952
00953 if (or_ == 1 && Chance16(1, 7)) or_ = 2;
00954
00955 if (direction == AXIS_X) {
00956 SetFenceSE(tile, or_);
00957 } else {
00958 SetFenceSW(tile, or_);
00959 }
00960 }
00961
00962 tile += (direction == AXIS_X ? TileDiffXY(1, 0) : TileDiffXY(0, 1));
00963 } while (--size);
00964 }
00965
00966 static void PlantFarmField(TileIndex tile, IndustryID industry)
00967 {
00968 uint size_x, size_y;
00969 uint32 r;
00970 uint count;
00971 uint counter;
00972 uint field_type;
00973 int type;
00974
00975 if (_settings_game.game_creation.landscape == LT_ARCTIC) {
00976 if (GetTileZ(tile) + TILE_HEIGHT * 2 >= GetSnowLine())
00977 return;
00978 }
00979
00980
00981 r = (Random() & 0x303) + 0x404;
00982 if (_settings_game.game_creation.landscape == LT_ARCTIC) r += 0x404;
00983 size_x = GB(r, 0, 8);
00984 size_y = GB(r, 8, 8);
00985
00986
00987 tile -= TileDiffXY(size_x / 2, size_y / 2);
00988
00989 if (TileX(tile) + size_x >= MapSizeX() || TileY(tile) + size_y >= MapSizeY()) return;
00990
00991
00992 count = 0;
00993 TILE_LOOP(cur_tile, size_x, size_y, tile) {
00994 assert(cur_tile < MapSize());
00995 count += IsBadFarmFieldTile(cur_tile);
00996 }
00997 if (count * 2 >= size_x * size_y) return;
00998
00999
01000 r = Random();
01001 counter = GB(r, 5, 3);
01002 field_type = GB(r, 8, 8) * 9 >> 8;
01003
01004
01005 TILE_LOOP(cur_tile, size_x, size_y, tile) {
01006 assert(cur_tile < MapSize());
01007 if (!IsBadFarmFieldTile2(cur_tile)) {
01008 MakeField(cur_tile, field_type, industry);
01009 SetClearCounter(cur_tile, counter);
01010 MarkTileDirtyByTile(cur_tile);
01011 }
01012 }
01013
01014 type = 3;
01015 if (_settings_game.game_creation.landscape != LT_ARCTIC && _settings_game.game_creation.landscape != LT_TROPIC) {
01016 type = _plantfarmfield_type[Random() & 0xF];
01017 }
01018
01019 SetupFarmFieldFence(tile - TileDiffXY(1, 0), size_y, type, AXIS_Y);
01020 SetupFarmFieldFence(tile - TileDiffXY(0, 1), size_x, type, AXIS_X);
01021 SetupFarmFieldFence(tile + TileDiffXY(size_x - 1, 0), size_y, type, AXIS_Y);
01022 SetupFarmFieldFence(tile + TileDiffXY(0, size_y - 1), size_x, type, AXIS_X);
01023 }
01024
01025 void PlantRandomFarmField(const Industry *i)
01026 {
01027 int x = i->location.w / 2 + Random() % 31 - 16;
01028 int y = i->location.h / 2 + Random() % 31 - 16;
01029
01030 TileIndex tile = TileAddWrap(i->location.tile, x, y);
01031
01032 if (tile != INVALID_TILE) PlantFarmField(tile, i->index);
01033 }
01034
01041 static bool SearchLumberMillTrees(TileIndex tile, void *user_data)
01042 {
01043 if (IsTileType(tile, MP_TREES) && GetTreeGrowth(tile) > 2) {
01044 CompanyID old_company = _current_company;
01045
01046
01047 _current_company = OWNER_NONE;
01048 _industry_sound_ctr = 1;
01049 _industry_sound_tile = tile;
01050 SndPlayTileFx(SND_38_CHAINSAW, tile);
01051
01052 DoCommand(tile, 0, 0, DC_EXEC, CMD_LANDSCAPE_CLEAR);
01053
01054 _current_company = old_company;
01055 return true;
01056 }
01057 return false;
01058 }
01059
01064 static void ChopLumberMillTrees(Industry *i)
01065 {
01066 TileIndex tile = i->location.tile;
01067
01068 if (!IsIndustryCompleted(tile)) return;
01069
01070 if (CircularTileSearch(&tile, 40, SearchLumberMillTrees, NULL))
01071 i->produced_cargo_waiting[0] = min(0xffff, i->produced_cargo_waiting[0] + 45);
01072 }
01073
01074 static void ProduceIndustryGoods(Industry *i)
01075 {
01076 uint32 r;
01077 uint num;
01078 const IndustrySpec *indsp = GetIndustrySpec(i->type);
01079
01080
01081 if ((i->counter & 0x3F) == 0) {
01082 if (Chance16R(1, 14, r) && (num = indsp->number_of_sounds) != 0) {
01083 SndPlayTileFx(
01084 (SoundFx)(indsp->random_sounds[((r >> 16) * num) >> 16]),
01085 i->location.tile);
01086 }
01087 }
01088
01089 i->counter--;
01090
01091
01092 if ((i->counter & 0xFF) == 0) {
01093 if (HasBit(indsp->callback_mask, CBM_IND_PRODUCTION_256_TICKS)) IndustryProductionCallback(i, 1);
01094
01095 IndustryBehaviour indbehav = indsp->behaviour;
01096 i->produced_cargo_waiting[0] = min(0xffff, i->produced_cargo_waiting[0] + i->production_rate[0]);
01097 i->produced_cargo_waiting[1] = min(0xffff, i->produced_cargo_waiting[1] + i->production_rate[1]);
01098
01099 if ((indbehav & INDUSTRYBEH_PLANT_FIELDS) != 0) {
01100 bool plant;
01101 if (HasBit(indsp->callback_mask, CBM_IND_SPECIAL_EFFECT)) {
01102 plant = (GetIndustryCallback(CBID_INDUSTRY_SPECIAL_EFFECT, Random(), 0, i, i->type, i->location.tile) != 0);
01103 } else {
01104 plant = Chance16(1, 8);
01105 }
01106
01107 if (plant) PlantRandomFarmField(i);
01108 }
01109 if ((indbehav & INDUSTRYBEH_CUT_TREES) != 0) {
01110 bool cut = ((i->counter & 0x1FF) == 0);
01111 if (HasBit(indsp->callback_mask, CBM_IND_SPECIAL_EFFECT)) {
01112 cut = (GetIndustryCallback(CBID_INDUSTRY_SPECIAL_EFFECT, 0, 1, i, i->type, i->location.tile) != 0);
01113 }
01114
01115 if (cut) ChopLumberMillTrees(i);
01116 }
01117
01118 TriggerIndustry(i, INDUSTRY_TRIGGER_INDUSTRY_TICK);
01119 StartStopIndustryTileAnimation(i, IAT_INDUSTRY_TICK);
01120 }
01121 }
01122
01123 void OnTick_Industry()
01124 {
01125 Industry *i;
01126
01127 if (_industry_sound_ctr != 0) {
01128 _industry_sound_ctr++;
01129
01130 if (_industry_sound_ctr == 75) {
01131 SndPlayTileFx(SND_37_BALLOON_SQUEAK, _industry_sound_tile);
01132 } else if (_industry_sound_ctr == 160) {
01133 _industry_sound_ctr = 0;
01134 SndPlayTileFx(SND_36_CARTOON_CRASH, _industry_sound_tile);
01135 }
01136 }
01137
01138 if (_game_mode == GM_EDITOR) return;
01139
01140 FOR_ALL_INDUSTRIES(i) {
01141 ProduceIndustryGoods(i);
01142 }
01143 }
01144
01145 static bool CheckNewIndustry_NULL(TileIndex tile)
01146 {
01147 return true;
01148 }
01149
01150 static bool CheckNewIndustry_Forest(TileIndex tile)
01151 {
01152 if (_settings_game.game_creation.landscape == LT_ARCTIC) {
01153 if (GetTileZ(tile) < HighestSnowLine() + TILE_HEIGHT * 2U) {
01154 _error_message = STR_ERROR_FOREST_CAN_ONLY_BE_PLANTED;
01155 return false;
01156 }
01157 }
01158 return true;
01159 }
01160
01161 static bool CheckNewIndustry_OilRefinery(TileIndex tile)
01162 {
01163 if (_game_mode == GM_EDITOR) return true;
01164 if (DistanceFromEdge(TILE_ADDXY(tile, 1, 1)) < _settings_game.game_creation.oil_refinery_limit) return true;
01165
01166 _error_message = STR_ERROR_CAN_ONLY_BE_POSITIONED;
01167 return false;
01168 }
01169
01170 extern bool _ignore_restrictions;
01171
01172 static bool CheckNewIndustry_OilRig(TileIndex tile)
01173 {
01174 if (_game_mode == GM_EDITOR && _ignore_restrictions) return true;
01175 if (TileHeight(tile) == 0 &&
01176 DistanceFromEdge(TILE_ADDXY(tile, 1, 1)) < _settings_game.game_creation.oil_refinery_limit) return true;
01177
01178 _error_message = STR_ERROR_CAN_ONLY_BE_POSITIONED;
01179 return false;
01180 }
01181
01182 static bool CheckNewIndustry_Farm(TileIndex tile)
01183 {
01184 if (_settings_game.game_creation.landscape == LT_ARCTIC) {
01185 if (GetTileZ(tile) + TILE_HEIGHT * 2 >= HighestSnowLine()) {
01186 _error_message = STR_ERROR_SITE_UNSUITABLE;
01187 return false;
01188 }
01189 }
01190 return true;
01191 }
01192
01193 static bool CheckNewIndustry_Plantation(TileIndex tile)
01194 {
01195 if (GetTropicZone(tile) == TROPICZONE_DESERT) {
01196 _error_message = STR_ERROR_SITE_UNSUITABLE;
01197 return false;
01198 }
01199
01200 return true;
01201 }
01202
01203 static bool CheckNewIndustry_Water(TileIndex tile)
01204 {
01205 if (GetTropicZone(tile) != TROPICZONE_DESERT) {
01206 _error_message = STR_ERROR_CAN_ONLY_BE_BUILT_IN_DESERT;
01207 return false;
01208 }
01209
01210 return true;
01211 }
01212
01213 static bool CheckNewIndustry_Lumbermill(TileIndex tile)
01214 {
01215 if (GetTropicZone(tile) != TROPICZONE_RAINFOREST) {
01216 _error_message = STR_ERROR_CAN_ONLY_BE_BUILT_IN_RAINFOREST;
01217 return false;
01218 }
01219 return true;
01220 }
01221
01222 static bool CheckNewIndustry_BubbleGen(TileIndex tile)
01223 {
01224 return GetTileZ(tile) <= TILE_HEIGHT * 4;
01225 }
01226
01227 typedef bool CheckNewIndustryProc(TileIndex tile);
01228 static CheckNewIndustryProc * const _check_new_industry_procs[CHECK_END] = {
01229 CheckNewIndustry_NULL,
01230 CheckNewIndustry_Forest,
01231 CheckNewIndustry_OilRefinery,
01232 CheckNewIndustry_Farm,
01233 CheckNewIndustry_Plantation,
01234 CheckNewIndustry_Water,
01235 CheckNewIndustry_Lumbermill,
01236 CheckNewIndustry_BubbleGen,
01237 CheckNewIndustry_OilRig
01238 };
01239
01240 static const Town *CheckMultipleIndustryInTown(TileIndex tile, int type)
01241 {
01242 const Town *t;
01243 const Industry *i;
01244
01245 t = ClosestTownFromTile(tile, UINT_MAX);
01246
01247 if (_settings_game.economy.multiple_industry_per_town) return t;
01248
01249 FOR_ALL_INDUSTRIES(i) {
01250 if (i->type == (byte)type &&
01251 i->town == t) {
01252 _error_message = STR_ERROR_ONLY_ONE_ALLOWED_PER_TOWN;
01253 return NULL;
01254 }
01255 }
01256
01257 return t;
01258 }
01259
01260 bool IsSlopeRefused(Slope current, Slope refused)
01261 {
01262 if (IsSteepSlope(current)) return true;
01263 if (current != SLOPE_FLAT) {
01264 if (IsSteepSlope(refused)) return true;
01265
01266 Slope t = ComplementSlope(current);
01267
01268 if ((refused & SLOPE_W) && (t & SLOPE_NW)) return true;
01269 if ((refused & SLOPE_S) && (t & SLOPE_NE)) return true;
01270 if ((refused & SLOPE_E) && (t & SLOPE_SW)) return true;
01271 if ((refused & SLOPE_N) && (t & SLOPE_SE)) return true;
01272 }
01273
01274 return false;
01275 }
01276
01277 static bool CheckIfIndustryTilesAreFree(TileIndex tile, const IndustryTileTable *it, uint itspec_index, int type, bool *custom_shape_check = NULL)
01278 {
01279 _error_message = STR_ERROR_SITE_UNSUITABLE;
01280 bool refused_slope = false;
01281 bool custom_shape = false;
01282
01283 do {
01284 IndustryGfx gfx = GetTranslatedIndustryTileID(it->gfx);
01285 TileIndex cur_tile = TileAddWrap(tile, it->ti.x, it->ti.y);
01286
01287 if (!IsValidTile(cur_tile)) {
01288 return false;
01289 }
01290
01291 if (gfx == GFX_WATERTILE_SPECIALCHECK) {
01292 if (!IsTileType(cur_tile, MP_WATER) ||
01293 GetTileSlope(cur_tile, NULL) != SLOPE_FLAT) {
01294 return false;
01295 }
01296 } else {
01297 if (!EnsureNoVehicleOnGround(cur_tile)) return false;
01298 if (MayHaveBridgeAbove(cur_tile) && IsBridgeAbove(cur_tile)) return false;
01299
01300 const IndustryTileSpec *its = GetIndustryTileSpec(gfx);
01301
01302 IndustryBehaviour ind_behav = GetIndustrySpec(type)->behaviour;
01303
01304
01305 if (!HasBit(its->slopes_refused, 5) && (IsWaterTile(cur_tile) == !(ind_behav & INDUSTRYBEH_BUILT_ONWATER))) return false;
01306
01307 if (HasBit(its->callback_mask, CBM_INDT_SHAPE_CHECK)) {
01308 custom_shape = true;
01309 if (!PerformIndustryTileSlopeCheck(tile, cur_tile, its, type, gfx, itspec_index)) return false;
01310 } else {
01311 Slope tileh = GetTileSlope(cur_tile, NULL);
01312 refused_slope |= IsSlopeRefused(tileh, its->slopes_refused);
01313 }
01314
01315 if ((ind_behav & (INDUSTRYBEH_ONLY_INTOWN | INDUSTRYBEH_TOWN1200_MORE)) ||
01316 ((ind_behav & INDUSTRYBEH_ONLY_NEARTOWN) && IsTileType(cur_tile, MP_HOUSE))) {
01317 if (!IsTileType(cur_tile, MP_HOUSE)) {
01318 _error_message = STR_ERROR_CAN_ONLY_BE_BUILT_IN_TOWNS;
01319 return false;
01320 }
01321
01322
01323 CompanyID old_company = _current_company;
01324 _current_company = OWNER_TOWN;
01325 bool not_clearable = DoCommand(cur_tile, 0, 0, DC_NONE, CMD_LANDSCAPE_CLEAR).Failed();
01326 _current_company = old_company;
01327
01328 if (not_clearable) return false;
01329 } else {
01330
01331 bool not_clearable = DoCommand(cur_tile, 0, 0, DC_AUTO | DC_NO_TEST_TOWN_RATING | DC_NO_MODIFY_TOWN_RATING, CMD_LANDSCAPE_CLEAR).Failed();
01332
01333 if (not_clearable) return false;
01334 }
01335 }
01336 } while ((++it)->ti.x != -0x80);
01337
01338 if (custom_shape_check != NULL) *custom_shape_check = custom_shape;
01339
01340
01341
01342
01343 return !refused_slope || (_settings_game.game_creation.land_generator == LG_TERRAGENESIS && _generating_world && !custom_shape && !_ignore_restrictions);
01344 }
01345
01346 static bool CheckIfIndustryIsAllowed(TileIndex tile, int type, const Town *t)
01347 {
01348 if ((GetIndustrySpec(type)->behaviour & INDUSTRYBEH_TOWN1200_MORE) && t->population < 1200) {
01349 _error_message = STR_ERROR_CAN_ONLY_BE_BUILT_IN_TOWNS_WITH_POPULATION_OF_1200;
01350 return false;
01351 }
01352
01353 if ((GetIndustrySpec(type)->behaviour & INDUSTRYBEH_ONLY_NEARTOWN) && DistanceMax(t->xy, tile) > 9) {
01354 _error_message = STR_ERROR_SITE_UNSUITABLE;
01355 return false;
01356 }
01357
01358 return true;
01359 }
01360
01361 static bool CheckCanTerraformSurroundingTiles(TileIndex tile, uint height, int internal)
01362 {
01363 int size_x, size_y;
01364 uint curh;
01365
01366 size_x = 2;
01367 size_y = 2;
01368
01369
01370 if (TileX(tile) == 0 || TileY(tile) == 0 || GetTileType(tile) == MP_VOID) return false;
01371
01372 tile += TileDiffXY(-1, -1);
01373 TILE_LOOP(tile_walk, size_x, size_y, tile) {
01374 curh = TileHeight(tile_walk);
01375
01376 if ((GetTileType(tile_walk) != MP_CLEAR) && (GetTileType(tile_walk) != MP_TREES))
01377 return false;
01378
01379
01380 if (internal != 0 && Delta(curh, height) > 1) return false;
01381
01382
01383
01384
01385 if (internal == 0 && curh != height) {
01386 if (TileX(tile_walk) == 0 || TileY(tile_walk) == 0 || !CheckCanTerraformSurroundingTiles(tile_walk + TileDiffXY(-1, -1), height, internal + 1))
01387 return false;
01388 }
01389 }
01390
01391 return true;
01392 }
01393
01398 static bool CheckIfCanLevelIndustryPlatform(TileIndex tile, DoCommandFlag flags, const IndustryTileTable *it, int type)
01399 {
01400 const int MKEND = -0x80;
01401 int max_x = 0;
01402 int max_y = 0;
01403 TileIndex cur_tile;
01404 uint size_x, size_y;
01405 uint h, curh;
01406
01407
01408 do {
01409 if (it->gfx == 0xFF) continue;
01410 if (it->ti.x > max_x) max_x = it->ti.x;
01411 if (it->ti.y > max_y) max_y = it->ti.y;
01412 } while ((++it)->ti.x != MKEND);
01413
01414
01415 h = TileHeight(tile);
01416
01417 if (TileX(tile) <= 1 || TileY(tile) <= 1) return false;
01418
01419
01420 cur_tile = tile + TileDiffXY(-1, -1);
01421 size_x = max_x + 4;
01422 size_y = max_y + 4;
01423
01424
01425 if (TileX(cur_tile) + size_x >= MapMaxX() || TileY(cur_tile) + size_y >= MapMaxY()) return false;
01426
01427
01428
01429 CompanyID old_company = _current_company;
01430 _current_company = OWNER_TOWN;
01431
01432 TILE_LOOP(tile_walk, size_x, size_y, cur_tile) {
01433 curh = TileHeight(tile_walk);
01434 if (curh != h) {
01435
01436
01437 if (!CheckCanTerraformSurroundingTiles(tile_walk, h, 0)) {
01438 _current_company = old_company;
01439 return false;
01440 }
01441
01442
01443 if (DoCommand(tile_walk, SLOPE_N, (curh > h) ? 0 : 1, flags & ~DC_EXEC, CMD_TERRAFORM_LAND).Failed()) {
01444 _current_company = old_company;
01445 return false;
01446 }
01447 }
01448 }
01449
01450 if (flags & DC_EXEC) {
01451
01452 TILE_LOOP(tile_walk, size_x, size_y, cur_tile) {
01453 curh = TileHeight(tile_walk);
01454 while (curh != h) {
01455
01456
01457
01458 DoCommand(tile_walk, SLOPE_N, (curh > h) ? 0 : 1, flags, CMD_TERRAFORM_LAND);
01459 curh += (curh > h) ? -1 : 1;
01460 }
01461 }
01462 }
01463
01464 _current_company = old_company;
01465 return true;
01466 }
01467
01468
01469 static bool CheckIfFarEnoughFromIndustry(TileIndex tile, int type)
01470 {
01471 const IndustrySpec *indspec = GetIndustrySpec(type);
01472 const Industry *i;
01473
01474 if (_settings_game.economy.same_industry_close && indspec->IsRawIndustry())
01475
01476 return true;
01477
01478 FOR_ALL_INDUSTRIES(i) {
01479
01480 bool in_low_distance = DistanceMax(tile, i->location.tile) <= 14;
01481
01482
01483 if (in_low_distance &&
01484 !indspec->IsRawIndustry() &&
01485 indspec->accepts_cargo[0] == i->accepts_cargo[0] && (
01486
01487 _game_mode != GM_EDITOR ||
01488 !_settings_game.economy.same_industry_close ||
01489 !_settings_game.economy.multiple_industry_per_town)) {
01490 _error_message = STR_ERROR_INDUSTRY_TOO_CLOSE;
01491 return false;
01492 }
01493
01494
01495 if ((i->type == indspec->conflicting[0] ||
01496 i->type == indspec->conflicting[1] ||
01497 i->type == indspec->conflicting[2]) &&
01498 in_low_distance) {
01499 _error_message = STR_ERROR_INDUSTRY_TOO_CLOSE;
01500 return false;
01501 }
01502 }
01503 return true;
01504 }
01505
01509 enum ProductionLevels {
01510 PRODLEVEL_CLOSURE = 0x00,
01511 PRODLEVEL_MINIMUM = 0x04,
01512 PRODLEVEL_DEFAULT = 0x10,
01513 PRODLEVEL_MAXIMUM = 0x80,
01514 };
01515
01516 static void DoCreateNewIndustry(Industry *i, TileIndex tile, int type, const IndustryTileTable *it, byte layout, const Town *t, Owner owner, Owner founder)
01517 {
01518 const IndustrySpec *indspec = GetIndustrySpec(type);
01519 uint32 r;
01520 uint j;
01521
01522 i->location = TileArea(tile, 1, 1);
01523 i->type = type;
01524 IncIndustryTypeCount(type);
01525
01526 i->produced_cargo[0] = indspec->produced_cargo[0];
01527 i->produced_cargo[1] = indspec->produced_cargo[1];
01528 i->accepts_cargo[0] = indspec->accepts_cargo[0];
01529 i->accepts_cargo[1] = indspec->accepts_cargo[1];
01530 i->accepts_cargo[2] = indspec->accepts_cargo[2];
01531 i->production_rate[0] = indspec->production_rate[0];
01532 i->production_rate[1] = indspec->production_rate[1];
01533
01534
01535 if (_settings_game.economy.smooth_economy &&
01536 !(HasBit(indspec->callback_mask, CBM_IND_PRODUCTION_256_TICKS) || HasBit(indspec->callback_mask, CBM_IND_PRODUCTION_CARGO_ARRIVAL)) &&
01537 !(HasBit(indspec->callback_mask, CBM_IND_MONTHLYPROD_CHANGE) || HasBit(indspec->callback_mask, CBM_IND_PRODUCTION_CHANGE))
01538 ) {
01539 i->production_rate[0] = min((RandomRange(256) + 128) * i->production_rate[0] >> 8, 255);
01540 i->production_rate[1] = min((RandomRange(256) + 128) * i->production_rate[1] >> 8, 255);
01541 }
01542
01543 i->town = t;
01544 i->owner = owner;
01545
01546 r = Random();
01547 i->random_colour = GB(r, 0, 4);
01548 i->counter = GB(r, 4, 12);
01549 i->random = GB(r, 16, 16);
01550 i->produced_cargo_waiting[0] = 0;
01551 i->produced_cargo_waiting[1] = 0;
01552 i->incoming_cargo_waiting[0] = 0;
01553 i->incoming_cargo_waiting[1] = 0;
01554 i->incoming_cargo_waiting[2] = 0;
01555 i->this_month_production[0] = 0;
01556 i->this_month_production[1] = 0;
01557 i->this_month_transported[0] = 0;
01558 i->this_month_transported[1] = 0;
01559 i->last_month_pct_transported[0] = 0;
01560 i->last_month_pct_transported[1] = 0;
01561 i->last_month_transported[0] = 0;
01562 i->last_month_transported[1] = 0;
01563 i->was_cargo_delivered = false;
01564 i->last_prod_year = _cur_year;
01565 i->last_month_production[0] = i->production_rate[0] * 8;
01566 i->last_month_production[1] = i->production_rate[1] * 8;
01567 i->founder = founder;
01568
01569 if (HasBit(indspec->callback_mask, CBM_IND_DECIDE_COLOUR)) {
01570 uint16 res = GetIndustryCallback(CBID_INDUSTRY_DECIDE_COLOUR, 0, 0, i, type, INVALID_TILE);
01571 if (res != CALLBACK_FAILED) i->random_colour = GB(res, 0, 4);
01572 }
01573
01574 if (HasBit(indspec->callback_mask, CBM_IND_INPUT_CARGO_TYPES)) {
01575 for (j = 0; j < lengthof(i->accepts_cargo); j++) i->accepts_cargo[j] = CT_INVALID;
01576 for (j = 0; j < lengthof(i->accepts_cargo); j++) {
01577 uint16 res = GetIndustryCallback(CBID_INDUSTRY_INPUT_CARGO_TYPES, j, 0, i, type, INVALID_TILE);
01578 if (res == CALLBACK_FAILED || GB(res, 0, 8) == CT_INVALID) break;
01579 i->accepts_cargo[j] = GetCargoTranslation(GB(res, 0, 8), indspec->grf_prop.grffile);
01580 }
01581 }
01582
01583 if (HasBit(indspec->callback_mask, CBM_IND_OUTPUT_CARGO_TYPES)) {
01584 for (j = 0; j < lengthof(i->produced_cargo); j++) i->produced_cargo[j] = CT_INVALID;
01585 for (j = 0; j < lengthof(i->produced_cargo); j++) {
01586 uint16 res = GetIndustryCallback(CBID_INDUSTRY_OUTPUT_CARGO_TYPES, j, 0, i, type, INVALID_TILE);
01587 if (res == CALLBACK_FAILED || GB(res, 0, 8) == CT_INVALID) break;
01588 i->produced_cargo[j] = GetCargoTranslation(GB(res, 0, 8), indspec->grf_prop.grffile);
01589 }
01590 }
01591
01592 i->construction_date = _date;
01593 i->construction_type = (_game_mode == GM_EDITOR) ? ICT_SCENARIO_EDITOR :
01594 (_generating_world ? ICT_MAP_GENERATION : ICT_NORMAL_GAMEPLAY);
01595
01596
01597
01598
01599 i->selected_layout = layout + 1;
01600
01601 if (!_generating_world) i->last_month_production[0] = i->last_month_production[1] = 0;
01602
01603 i->prod_level = PRODLEVEL_DEFAULT;
01604
01605 do {
01606 TileIndex cur_tile = tile + ToTileIndexDiff(it->ti);
01607
01608 if (it->gfx != GFX_WATERTILE_SPECIALCHECK) {
01609 i->location.Add(cur_tile);
01610
01611 WaterClass wc = (IsWaterTile(cur_tile) ? GetWaterClass(cur_tile) : WATER_CLASS_INVALID);
01612
01613 DoCommand(cur_tile, 0, 0, DC_EXEC | DC_NO_TEST_TOWN_RATING | DC_NO_MODIFY_TOWN_RATING, CMD_LANDSCAPE_CLEAR);
01614
01615 MakeIndustry(cur_tile, i->index, it->gfx, Random(), wc);
01616
01617 if (_generating_world) {
01618 SetIndustryConstructionCounter(cur_tile, 3);
01619 SetIndustryConstructionStage(cur_tile, 2);
01620 }
01621
01622
01623 IndustryGfx cur_gfx = GetTranslatedIndustryTileID(it->gfx);
01624 const IndustryTileSpec *its = GetIndustryTileSpec(cur_gfx);
01625 if (its->animation_info != 0xFFFF) AddAnimatedTile(cur_tile);
01626 }
01627 } while ((++it)->ti.x != -0x80);
01628
01629 if (GetIndustrySpec(i->type)->behaviour & INDUSTRYBEH_PLANT_ON_BUILT) {
01630 for (j = 0; j != 50; j++) PlantRandomFarmField(i);
01631 }
01632 InvalidateWindowData(WC_INDUSTRY_DIRECTORY, 0, 0);
01633
01634 Station::RecomputeIndustriesNearForAll();
01635 }
01636
01647 static Industry *CreateNewIndustryHelper(TileIndex tile, IndustryType type, DoCommandFlag flags, const IndustrySpec *indspec, uint itspec_index, uint32 seed, Owner founder)
01648 {
01649 assert(itspec_index < indspec->num_table);
01650 const IndustryTileTable *it = indspec->table[itspec_index];
01651 bool custom_shape_check = false;
01652
01653 if (!CheckIfIndustryTilesAreFree(tile, it, itspec_index, type, &custom_shape_check)) return NULL;
01654
01655 if (HasBit(GetIndustrySpec(type)->callback_mask, CBM_IND_LOCATION)) {
01656 if (!CheckIfCallBackAllowsCreation(tile, type, itspec_index, seed)) return NULL;
01657 } else {
01658 if (!_check_new_industry_procs[indspec->check_proc](tile)) return NULL;
01659 }
01660
01661 if (!custom_shape_check && _settings_game.game_creation.land_generator == LG_TERRAGENESIS && _generating_world && !_ignore_restrictions && !CheckIfCanLevelIndustryPlatform(tile, DC_NO_WATER, it, type)) return NULL;
01662 if (!CheckIfFarEnoughFromIndustry(tile, type)) return NULL;
01663
01664 const Town *t = CheckMultipleIndustryInTown(tile, type);
01665 if (t == NULL) return NULL;
01666
01667 if (!CheckIfIndustryIsAllowed(tile, type, t)) return NULL;
01668
01669 if (!Industry::CanAllocateItem()) return NULL;
01670
01671 if (flags & DC_EXEC) {
01672 Industry *i = new Industry(tile);
01673 if (!custom_shape_check) CheckIfCanLevelIndustryPlatform(tile, DC_NO_WATER | DC_EXEC, it, type);
01674 DoCreateNewIndustry(i, tile, type, it, itspec_index, t, OWNER_NONE, founder);
01675
01676 return i;
01677 }
01678
01679
01680
01681 return (Industry *)-1;
01682 }
01683
01694 CommandCost CmdBuildIndustry(TileIndex tile, DoCommandFlag flags, uint32 p1, uint32 p2, const char *text)
01695 {
01696 IndustryType it = GB(p1, 0, 8);
01697 if (it >= NUM_INDUSTRYTYPES) return CMD_ERROR;
01698
01699 const IndustrySpec *indspec = GetIndustrySpec(it);
01700
01701
01702 if (!indspec->enabled || indspec->num_table == 0) return CMD_ERROR;
01703
01704
01705
01706 if (_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 0 && indspec->IsRawIndustry()) {
01707 return CMD_ERROR;
01708 }
01709
01710 const Industry *ind = NULL;
01711 if (_game_mode != GM_EDITOR && _settings_game.construction.raw_industry_construction == 2 && indspec->IsRawIndustry()) {
01712 if (flags & DC_EXEC) {
01713
01714 CompanyID founder = _current_company;
01715 _current_company = OWNER_TOWN;
01716
01717
01718
01719 if (Random() <= indspec->prospecting_chance) {
01720 for (int i = 0; i < 5000; i++) {
01721
01722
01723
01724 tile = RandomTile();
01725 ind = CreateNewIndustryHelper(tile, it, flags, indspec, RandomRange(indspec->num_table), p2, founder);
01726 if (ind != NULL) {
01727 break;
01728 }
01729 }
01730 }
01731 _current_company = founder;
01732 }
01733 } else {
01734 int count = indspec->num_table;
01735 const IndustryTileTable * const *itt = indspec->table;
01736 int num = GB(p1, 8, 8);
01737 if (num >= count) return CMD_ERROR;
01738
01739 _error_message = STR_ERROR_SITE_UNSUITABLE;
01740 do {
01741 if (--count < 0) return CMD_ERROR;
01742 if (--num < 0) num = indspec->num_table - 1;
01743 } while (!CheckIfIndustryTilesAreFree(tile, itt[num], num, it));
01744
01745 ind = CreateNewIndustryHelper(tile, it, flags, indspec, num, p2, _current_company);
01746 if (ind == NULL) return CMD_ERROR;
01747 }
01748
01749 if ((flags & DC_EXEC) && _game_mode != GM_EDITOR && ind != NULL) {
01750 SetDParam(0, indspec->name);
01751 if (indspec->new_industry_text > STR_LAST_STRINGID) {
01752 SetDParam(1, STR_TOWN_NAME);
01753 SetDParam(2, ind->town->index);
01754 } else {
01755 SetDParam(1, ind->town->index);
01756 }
01757 AddIndustryNewsItem(indspec->new_industry_text, NS_INDUSTRY_OPEN, ind->index);
01758 AI::BroadcastNewEvent(new AIEventIndustryOpen(ind->index));
01759 }
01760
01761 return CommandCost(EXPENSES_OTHER, indspec->GetConstructionCost());
01762 }
01763
01764
01765 static Industry *CreateNewIndustry(TileIndex tile, IndustryType type)
01766 {
01767 const IndustrySpec *indspec = GetIndustrySpec(type);
01768
01769 uint32 seed = Random();
01770 return CreateNewIndustryHelper(tile, type, DC_EXEC, indspec, RandomRange(indspec->num_table), seed, OWNER_NONE);
01771 }
01772
01773 enum {
01774 NB_NUMOFINDUSTRY = 11,
01775 NB_DIFFICULTY_LEVEL = 5,
01776 };
01777
01778 static const byte _numof_industry_table[NB_DIFFICULTY_LEVEL][NB_NUMOFINDUSTRY] = {
01779
01780 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0},
01781 {0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1},
01782 {0, 1, 1, 1, 2, 2, 3, 3, 4, 4, 5},
01783 {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10},
01784 {0, 2, 3, 4, 6, 7, 8, 9, 10, 10, 10},
01785 };
01786
01791 static void PlaceInitialIndustry(IndustryType type, int amount)
01792 {
01793
01794
01795 int num = (amount > NB_NUMOFINDUSTRY) ? amount : _numof_industry_table[_settings_game.difficulty.number_industries][amount];
01796 const IndustrySpec *ind_spc = GetIndustrySpec(type);
01797
01798
01799 num = (ind_spc->check_proc == CHECK_REFINERY || ind_spc->check_proc == CHECK_OIL_RIG) ? ScaleByMapSize1D(num) : ScaleByMapSize(num);
01800
01801 if (_settings_game.difficulty.number_industries != 0) {
01802 CompanyID old_company = _current_company;
01803 _current_company = OWNER_NONE;
01804 assert(num > 0);
01805
01806 do {
01807 uint i;
01808
01809 IncreaseGeneratingWorldProgress(GWP_INDUSTRY);
01810
01811 for (i = 0; i < 2000; i++) {
01812 if (CreateNewIndustry(RandomTile(), type) != NULL) break;
01813 }
01814 } while (--num);
01815
01816 _current_company = old_company;
01817 }
01818 }
01819
01822 void GenerateIndustries()
01823 {
01824 uint i = 0;
01825 uint8 chance;
01826 IndustryType it;
01827 const IndustrySpec *ind_spc;
01828
01829
01830 if (_settings_game.difficulty.number_industries > 0) {
01831 for (it = 0; it < NUM_INDUSTRYTYPES; it++) {
01832
01833 ind_spc = GetIndustrySpec(it);
01834
01835 if (!CheckIfCallBackAllowsAvailability(it, IACT_MAPGENERATION)) {
01836 ResetIndustryCreationProbility(it);
01837 }
01838
01839 chance = ind_spc->appear_creation[_settings_game.game_creation.landscape];
01840 if (ind_spc->enabled && chance > 0 && ind_spc->num_table > 0) {
01841
01842
01843
01844 int num = (chance > NB_NUMOFINDUSTRY) ? chance : _numof_industry_table[_settings_game.difficulty.number_industries][chance];
01845
01846
01847 num = (ind_spc->check_proc == CHECK_REFINERY || ind_spc->check_proc == CHECK_OIL_RIG) ? ScaleByMapSize1D(num) : ScaleByMapSize(num);
01848 i += num;
01849 }
01850 }
01851 }
01852
01853 SetGeneratingWorldProgress(GWP_INDUSTRY, i);
01854
01855 if (_settings_game.difficulty.number_industries > 0) {
01856 for (it = 0; it < NUM_INDUSTRYTYPES; it++) {
01857
01858
01859
01860
01861
01862 ind_spc = GetIndustrySpec(it);
01863 if (ind_spc->enabled && ind_spc->num_table > 0) {
01864 chance = ind_spc->appear_creation[_settings_game.game_creation.landscape];
01865 if (chance > 0) PlaceInitialIndustry(it, chance);
01866 }
01867 }
01868 }
01869 }
01870
01871 static void UpdateIndustryStatistics(Industry *i)
01872 {
01873 byte pct;
01874 bool refresh = false;
01875
01876 for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
01877 if (i->produced_cargo[j] != CT_INVALID) {
01878 pct = 0;
01879 if (i->this_month_production[j] != 0) {
01880 i->last_prod_year = _cur_year;
01881 pct = min(i->this_month_transported[j] * 256 / i->this_month_production[j], 255);
01882 }
01883 i->last_month_pct_transported[j] = pct;
01884
01885 i->last_month_production[j] = i->this_month_production[j];
01886 i->this_month_production[j] = 0;
01887
01888 i->last_month_transported[j] = i->this_month_transported[j];
01889 i->this_month_transported[j] = 0;
01890 refresh = true;
01891 }
01892 }
01893
01894 if (refresh) SetWindowDirty(WC_INDUSTRY_VIEW, i->index);
01895 }
01896
01898 struct ProbabilityHelper {
01899 uint16 prob;
01900 IndustryType ind;
01901 };
01902
01906 static void MaybeNewIndustry()
01907 {
01908 Industry *ind;
01909 IndustryType rndtype, j;
01910 const IndustrySpec *ind_spc;
01911 uint num = 0;
01912 ProbabilityHelper cumulative_probs[NUM_INDUSTRYTYPES];
01913 uint16 probability_max = 0;
01914
01915
01916 for (j = 0; j < NUM_INDUSTRYTYPES; j++) {
01917 ind_spc = GetIndustrySpec(j);
01918 byte chance = ind_spc->appear_ingame[_settings_game.game_creation.landscape];
01919
01920 if (!ind_spc->enabled || chance == 0 || ind_spc->num_table == 0) continue;
01921
01922
01923
01924 if (CheckIfCallBackAllowsAvailability(j, IACT_RANDOMCREATION)) {
01925 probability_max += chance;
01926
01927 cumulative_probs[num].ind = j;
01928 cumulative_probs[num++].prob = probability_max;
01929 }
01930 }
01931
01932
01933 if (probability_max == 0) return;
01934
01935
01936 rndtype = RandomRange(probability_max);
01937 for (j = 0; j < NUM_INDUSTRYTYPES; j++) {
01938
01939 if (cumulative_probs[j].prob >= rndtype) break;
01940 }
01941
01942 ind_spc = GetIndustrySpec(cumulative_probs[j].ind);
01943
01944 if ((ind_spc->behaviour & INDUSTRYBEH_BEFORE_1950) && _cur_year > 1950) return;
01945 if ((ind_spc->behaviour & INDUSTRYBEH_AFTER_1960) && _cur_year < 1960) return;
01946
01947
01948 num = 2000;
01949 for (;;) {
01950 ind = CreateNewIndustry(RandomTile(), cumulative_probs[j].ind);
01951 if (ind != NULL) break;
01952 if (--num == 0) return;
01953 }
01954
01955 SetDParam(0, ind_spc->name);
01956 if (ind_spc->new_industry_text > STR_LAST_STRINGID) {
01957 SetDParam(1, STR_TOWN_NAME);
01958 SetDParam(2, ind->town->index);
01959 } else {
01960 SetDParam(1, ind->town->index);
01961 }
01962 AddIndustryNewsItem(ind_spc->new_industry_text, NS_INDUSTRY_OPEN, ind->index);
01963 AI::BroadcastNewEvent(new AIEventIndustryOpen(ind->index));
01964 }
01965
01974 static bool CheckIndustryCloseDownProtection(IndustryType type)
01975 {
01976 const IndustrySpec *indspec = GetIndustrySpec(type);
01977
01978
01979 if ((indspec->behaviour & INDUSTRYBEH_DONT_INCR_PROD) && _settings_game.game_creation.landscape == LT_TEMPERATE) return false;
01980 return (indspec->behaviour & INDUSTRYBEH_CANCLOSE_LASTINSTANCE) == 0 && GetIndustryTypeCount(type) <= 1;
01981 }
01982
01992 static void CanCargoServiceIndustry(CargoID cargo, Industry *ind, bool *c_accepts, bool *c_produces)
01993 {
01994 const IndustrySpec *indspec = GetIndustrySpec(ind->type);
01995
01996
01997 for (byte j = 0; j < lengthof(ind->accepts_cargo); j++) {
01998 if (ind->accepts_cargo[j] == CT_INVALID) continue;
01999 if (cargo == ind->accepts_cargo[j]) {
02000 if (HasBit(indspec->callback_mask, CBM_IND_REFUSE_CARGO)) {
02001 uint16 res = GetIndustryCallback(CBID_INDUSTRY_REFUSE_CARGO,
02002 0, GetReverseCargoTranslation(cargo, indspec->grf_prop.grffile),
02003 ind, ind->type, ind->location.tile);
02004 if (res == 0) continue;
02005 }
02006 *c_accepts = true;
02007 break;
02008 }
02009 }
02010
02011
02012 for (byte j = 0; j < lengthof(ind->produced_cargo); j++) {
02013 if (ind->produced_cargo[j] == CT_INVALID) continue;
02014 if (cargo == ind->produced_cargo[j]) {
02015 *c_produces = true;
02016 break;
02017 }
02018 }
02019 }
02020
02034 static int WhoCanServiceIndustry(Industry *ind)
02035 {
02036
02037 StationList stations;
02038 FindStationsAroundTiles(ind->location, &stations);
02039
02040 if (stations.Length() == 0) return 0;
02041
02042 const Vehicle *v;
02043 int result = 0;
02044 FOR_ALL_VEHICLES(v) {
02045
02046 if (v->owner != _local_company && result != 0) continue;
02047
02048
02049 bool c_accepts = false;
02050 bool c_produces = false;
02051 if (v->type == VEH_TRAIN && Train::From(v)->IsFrontEngine()) {
02052 for (const Vehicle *u = v; u != NULL; u = u->Next()) {
02053 CanCargoServiceIndustry(u->cargo_type, ind, &c_accepts, &c_produces);
02054 }
02055 } else if (v->type == VEH_ROAD || v->type == VEH_SHIP || v->type == VEH_AIRCRAFT) {
02056 CanCargoServiceIndustry(v->cargo_type, ind, &c_accepts, &c_produces);
02057 } else {
02058 continue;
02059 }
02060 if (!c_accepts && !c_produces) continue;
02061
02062
02063
02064
02065
02066 const Order *o;
02067 FOR_VEHICLE_ORDERS(v, o) {
02068 if (o->IsType(OT_GOTO_STATION) && !(o->GetUnloadType() & OUFB_TRANSFER)) {
02069
02070 Station *st = Station::Get(o->GetDestination());
02071 assert(st != NULL);
02072
02073
02074 if ((o->GetUnloadType() & OUFB_UNLOAD) && !c_accepts) break;
02075
02076 if (stations.Contains(st)) {
02077 if (v->owner == _local_company) return 2;
02078 result = 1;
02079 }
02080 }
02081 }
02082 }
02083 return result;
02084 }
02085
02093 static void ReportNewsProductionChangeIndustry(Industry *ind, CargoID type, int percent)
02094 {
02095 NewsSubtype ns;
02096
02097 switch (WhoCanServiceIndustry(ind)) {
02098 case 0: ns = NS_INDUSTRY_NOBODY; break;
02099 case 1: ns = NS_INDUSTRY_OTHER; break;
02100 case 2: ns = NS_INDUSTRY_COMPANY; break;
02101 default: NOT_REACHED();
02102 }
02103 SetDParam(2, abs(percent));
02104 SetDParam(0, CargoSpec::Get(type)->name);
02105 SetDParam(1, ind->index);
02106 AddIndustryNewsItem(
02107 percent >= 0 ? STR_NEWS_INDUSTRY_PRODUCTION_INCREASE_SMOOTH : STR_NEWS_INDUSTRY_PRODUCTION_DECREASE_SMOOTH,
02108 ns,
02109 ind->index
02110 );
02111 }
02112
02113 enum {
02114 PERCENT_TRANSPORTED_60 = 153,
02115 PERCENT_TRANSPORTED_80 = 204,
02116 };
02117
02122 static void ChangeIndustryProduction(Industry *i, bool monthly)
02123 {
02124 StringID str = STR_NULL;
02125 bool closeit = false;
02126 const IndustrySpec *indspec = GetIndustrySpec(i->type);
02127 bool standard = false;
02128 bool suppress_message = false;
02129 bool recalculate_multipliers = false;
02130
02131 bool smooth_economy = _settings_game.economy.smooth_economy &&
02132 !(HasBit(indspec->callback_mask, CBM_IND_PRODUCTION_256_TICKS) || HasBit(indspec->callback_mask, CBM_IND_PRODUCTION_CARGO_ARRIVAL)) &&
02133 !(HasBit(indspec->callback_mask, CBM_IND_MONTHLYPROD_CHANGE) || HasBit(indspec->callback_mask, CBM_IND_PRODUCTION_CHANGE));
02134 byte div = 0;
02135 byte mul = 0;
02136 int8 increment = 0;
02137
02138 bool callback_enabled = HasBit(indspec->callback_mask, monthly ? CBM_IND_MONTHLYPROD_CHANGE : CBM_IND_PRODUCTION_CHANGE);
02139 if (callback_enabled) {
02140 uint16 res = GetIndustryCallback(monthly ? CBID_INDUSTRY_MONTHLYPROD_CHANGE : CBID_INDUSTRY_PRODUCTION_CHANGE, 0, Random(), i, i->type, i->location.tile);
02141 if (res != CALLBACK_FAILED) {
02142 suppress_message = HasBit(res, 7);
02143
02144 if (HasBit(res, 8)) str = MapGRFStringID(indspec->grf_prop.grffile->grfid, GB(GetRegister(0x100), 0, 16));
02145 res = GB(res, 0, 4);
02146 switch (res) {
02147 default: NOT_REACHED();
02148 case 0x0: break;
02149 case 0x1: div = 1; break;
02150 case 0x2: mul = 1; break;
02151 case 0x3: closeit = true; break;
02152 case 0x4: standard = true; break;
02153 case 0x5: case 0x6: case 0x7:
02154 case 0x8: div = res - 0x3; break;
02155 case 0x9: case 0xA: case 0xB:
02156 case 0xC: mul = res - 0x7; break;
02157 case 0xD:
02158 case 0xE:
02159 increment = res == 0x0D ? -1 : 1;
02160 break;
02161 case 0xF:
02162 i->prod_level = Clamp(GB(GetRegister(0x100), 16, 8), PRODLEVEL_MINIMUM, PRODLEVEL_MAXIMUM);
02163 recalculate_multipliers = true;
02164 break;
02165 }
02166 }
02167 } else {
02168 if (monthly != smooth_economy) return;
02169 if (indspec->life_type == INDUSTRYLIFE_BLACK_HOLE) return;
02170 }
02171
02172 if (standard || (!callback_enabled && (indspec->life_type & (INDUSTRYLIFE_ORGANIC | INDUSTRYLIFE_EXTRACTIVE)) != 0)) {
02173
02174 bool only_decrease = (indspec->behaviour & INDUSTRYBEH_DONT_INCR_PROD) && _settings_game.game_creation.landscape == LT_TEMPERATE;
02175
02176 if (smooth_economy) {
02177 closeit = true;
02178 for (byte j = 0; j < lengthof(i->produced_cargo); j++) {
02179 if (i->produced_cargo[j] == CT_INVALID) continue;
02180 uint32 r = Random();
02181 int old_prod, new_prod, percent;
02182
02183 int mult = (i->last_month_pct_transported[j] > PERCENT_TRANSPORTED_60) ? 1 : -1;
02184
02185 new_prod = old_prod = i->production_rate[j];
02186
02187
02188
02189 if (only_decrease) {
02190 mult = -1;
02191
02192
02193 } else if (Chance16I(1, ((i->last_month_pct_transported[j] > PERCENT_TRANSPORTED_80) ? 6 : 3), r)) {
02194 mult *= -1;
02195 }
02196
02197
02198
02199 if (Chance16I(1, 22, r >> 16)) {
02200 new_prod += mult * (max(((RandomRange(50) + 10) * old_prod) >> 8, 1U));
02201 }
02202
02203
02204 new_prod = Clamp(new_prod, 1, 255);
02205
02206 if (((indspec->behaviour & INDUSTRYBEH_BUILT_ONWATER) != 0) && j == 1)
02207 new_prod = Clamp(new_prod, 0, 16);
02208
02209
02210 if (new_prod == old_prod && old_prod > 1) {
02211 closeit = false;
02212 continue;
02213 }
02214
02215 percent = (old_prod == 0) ? 100 : (new_prod * 100 / old_prod - 100);
02216 i->production_rate[j] = new_prod;
02217
02218
02219 if (new_prod > 1) closeit = false;
02220
02221 if (abs(percent) >= 10) {
02222 ReportNewsProductionChangeIndustry(i, i->produced_cargo[j], percent);
02223 }
02224 }
02225 } else {
02226 if (only_decrease || Chance16(1, 3)) {
02227
02228 if (!only_decrease && (i->last_month_pct_transported[0] > PERCENT_TRANSPORTED_60) != Chance16(1, 3)) {
02229 mul = 1;
02230 } else {
02231 div = 1;
02232 }
02233 }
02234 }
02235 }
02236
02237 if (!callback_enabled && (indspec->life_type & INDUSTRYLIFE_PROCESSING)) {
02238 if ( (byte)(_cur_year - i->last_prod_year) >= 5 && Chance16(1, smooth_economy ? 180 : 2)) {
02239 closeit = true;
02240 }
02241 }
02242
02243
02244 while (mul-- != 0 && i->prod_level < PRODLEVEL_MAXIMUM) {
02245 i->prod_level = min(i->prod_level * 2, PRODLEVEL_MAXIMUM);
02246 recalculate_multipliers = true;
02247 if (str == STR_NULL) str = indspec->production_up_text;
02248 }
02249
02250
02251 while (div-- != 0 && !closeit) {
02252 if (i->prod_level == PRODLEVEL_MINIMUM) {
02253 closeit = true;
02254 } else {
02255 i->prod_level = max(i->prod_level / 2, (int)PRODLEVEL_MINIMUM);
02256 recalculate_multipliers = true;
02257 if (str == STR_NULL) str = indspec->production_down_text;
02258 }
02259 }
02260
02261
02262 if (increment != 0) {
02263 if (increment < 0 && i->prod_level == PRODLEVEL_MINIMUM) {
02264 closeit = true;
02265 } else {
02266 i->prod_level = ClampU(i->prod_level + increment, PRODLEVEL_MINIMUM, PRODLEVEL_MAXIMUM);
02267 recalculate_multipliers = true;
02268 }
02269 }
02270
02271
02272
02273 if (recalculate_multipliers) {
02274
02275 i->production_rate[0] = min((indspec->production_rate[0] * i->prod_level + PRODLEVEL_DEFAULT - 1) / PRODLEVEL_DEFAULT, 0xFF);
02276 i->production_rate[1] = min((indspec->production_rate[1] * i->prod_level + PRODLEVEL_DEFAULT - 1) / PRODLEVEL_DEFAULT, 0xFF);
02277 }
02278
02279
02280 if (closeit && !CheckIndustryCloseDownProtection(i->type)) {
02281 i->prod_level = PRODLEVEL_CLOSURE;
02282 str = indspec->closure_text;
02283 }
02284
02285 if (!suppress_message && str != STR_NULL) {
02286 NewsSubtype ns;
02287
02288 if (closeit) {
02289 ns = NS_INDUSTRY_CLOSE;
02290 AI::BroadcastNewEvent(new AIEventIndustryClose(i->index));
02291 } else {
02292 switch (WhoCanServiceIndustry(i)) {
02293 case 0: ns = NS_INDUSTRY_NOBODY; break;
02294 case 1: ns = NS_INDUSTRY_OTHER; break;
02295 case 2: ns = NS_INDUSTRY_COMPANY; break;
02296 default: NOT_REACHED();
02297 }
02298 }
02299
02300 if (str > STR_LAST_STRINGID) {
02301 SetDParam(0, STR_TOWN_NAME);
02302 SetDParam(1, i->town->index);
02303 SetDParam(2, indspec->name);
02304 } else if (closeit) {
02305 SetDParam(0, STR_FORMAT_INDUSTRY_NAME);
02306 SetDParam(1, i->town->index);
02307 SetDParam(2, indspec->name);
02308 } else {
02309 SetDParam(0, i->index);
02310 }
02311
02312 AddNewsItem(str,
02313 ns,
02314 closeit ? NR_TILE : NR_INDUSTRY,
02315 closeit ? i->location.tile + TileDiffXY(1, 1) : i->index);
02316 }
02317 }
02318
02324 void IndustryDailyLoop()
02325 {
02326 _economy.industry_daily_change_counter += _economy.industry_daily_increment;
02327
02328
02329
02330
02331 uint16 change_loop = _economy.industry_daily_change_counter >> 16;
02332
02333
02334 _economy.industry_daily_change_counter &= 0xFFFF;
02335
02336 if (change_loop == 0) {
02337 return;
02338 }
02339
02340 CompanyID old_company = _current_company;
02341 _current_company = OWNER_NONE;
02342
02343
02344 for (uint16 j = 0; j < change_loop; j++) {
02345
02346 if (Chance16(3, 100)) {
02347 MaybeNewIndustry();
02348 } else {
02349 Industry *i = Industry::GetRandom();
02350 if (i != NULL) ChangeIndustryProduction(i, false);
02351 }
02352 }
02353
02354 _current_company = old_company;
02355
02356
02357 InvalidateWindowData(WC_INDUSTRY_DIRECTORY, 0, 1);
02358 }
02359
02360 void IndustryMonthlyLoop()
02361 {
02362 Industry *i;
02363 CompanyID old_company = _current_company;
02364 _current_company = OWNER_NONE;
02365
02366 FOR_ALL_INDUSTRIES(i) {
02367 UpdateIndustryStatistics(i);
02368 if (i->prod_level == PRODLEVEL_CLOSURE) {
02369 delete i;
02370 } else {
02371 ChangeIndustryProduction(i, true);
02372 }
02373 }
02374
02375 _current_company = old_company;
02376
02377
02378 InvalidateWindowData(WC_INDUSTRY_DIRECTORY, 0, 1);
02379 }
02380
02381
02382 void InitializeIndustries()
02383 {
02384 _industry_pool.CleanPool();
02385
02386 ResetIndustryCounts();
02387 _industry_sound_tile = 0;
02388 }
02389
02390 bool IndustrySpec::IsRawIndustry() const
02391 {
02392
02393 return (this->life_type & (INDUSTRYLIFE_EXTRACTIVE | INDUSTRYLIFE_ORGANIC)) != 0 &&
02394 (this->behaviour & INDUSTRYBEH_CUT_TREES) == 0;
02395 }
02396
02397 Money IndustrySpec::GetConstructionCost() const
02398 {
02399
02400 return (_price[(_settings_game.construction.raw_industry_construction == 1 && this->IsRawIndustry()) ?
02401 PR_BUILD_INDUSTRY_RAW : PR_BUILD_INDUSTRY] * this->cost_multiplier) >> 8;
02402 }
02403
02404 Money IndustrySpec::GetRemovalCost() const
02405 {
02406 return (_price[PR_CLEAR_INDUSTRY] * this->removal_cost_multiplier) >> 8;
02407 }
02408
02409 static CommandCost TerraformTile_Industry(TileIndex tile, DoCommandFlag flags, uint z_new, Slope tileh_new)
02410 {
02411 if (AutoslopeEnabled()) {
02412
02413
02414
02415
02416
02417
02418 Slope tileh_old = GetTileSlope(tile, NULL);
02419
02420 if (!IsSteepSlope(tileh_old) && !IsSteepSlope(tileh_new) && (GetTileMaxZ(tile) == z_new + GetSlopeMaxZ(tileh_new))) {
02421 const IndustryGfx gfx = GetIndustryGfx(tile);
02422 const IndustryTileSpec *itspec = GetIndustryTileSpec(gfx);
02423
02424
02425 if (HasBit(itspec->callback_mask, CBM_INDT_AUTOSLOPE)) {
02426
02427 uint16 res = GetIndustryTileCallback(CBID_INDUSTRY_AUTOSLOPE, 0, 0, gfx, Industry::GetByTile(tile), tile);
02428 if ((res == 0) || (res == CALLBACK_FAILED)) return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
02429 } else {
02430
02431 return CommandCost(EXPENSES_CONSTRUCTION, _price[PR_BUILD_FOUNDATION]);
02432 }
02433 }
02434 }
02435 return DoCommand(tile, 0, 0, flags, CMD_LANDSCAPE_CLEAR);
02436 }
02437
02438 extern const TileTypeProcs _tile_type_industry_procs = {
02439 DrawTile_Industry,
02440 GetSlopeZ_Industry,
02441 ClearTile_Industry,
02442 AddAcceptedCargo_Industry,
02443 GetTileDesc_Industry,
02444 GetTileTrackStatus_Industry,
02445 ClickTile_Industry,
02446 AnimateTile_Industry,
02447 TileLoop_Industry,
02448 ChangeTileOwner_Industry,
02449 NULL,
02450 NULL,
02451 GetFoundation_Industry,
02452 TerraformTile_Industry,
02453 };