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