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