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