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