00001
00002
00005 #include "stdafx.h"
00006 #include "debug.h"
00007 #include "tile_type.h"
00008 #include "strings_type.h"
00009 #include "company_type.h"
00010 #include "industry_map.h"
00011 #include "newgrf.h"
00012 #include "newgrf_industries.h"
00013 #include "newgrf_commons.h"
00014 #include "newgrf_text.h"
00015 #include "newgrf_town.h"
00016 #include "window_func.h"
00017 #include "town.h"
00018 #include "company_base.h"
00019 #include "command_func.h"
00020
00021 #include "table/strings.h"
00022
00023 static uint32 _industry_creation_random_bits;
00024
00025
00026
00027
00028 IndustryOverrideManager _industry_mngr(NEW_INDUSTRYOFFSET, NUM_INDUSTRYTYPES, INVALID_INDUSTRYTYPE);
00029 IndustryTileOverrideManager _industile_mngr(NEW_INDUSTRYTILEOFFSET, NUM_INDUSTRYTILES, INVALID_INDUSTRYTILE);
00030
00031 IndustryType MapNewGRFIndustryType(IndustryType grf_type, uint32 grf_id)
00032 {
00033 if (grf_type == IT_INVALID) return IT_INVALID;
00034 if (!HasBit(grf_type, 7)) return GB(grf_type, 0, 6);
00035
00036 return _industry_mngr.GetID(GB(grf_type, 0, 6), grf_id);
00037 }
00038
00046 static uint GetClosestWaterDistance(TileIndex tile, bool water)
00047 {
00048 if (IsTileType(tile, MP_WATER) == water) return 0;
00049
00050 uint max_dist = water ? 0x7F : 0x200;
00051
00052 int x = TileX(tile);
00053 int y = TileY(tile);
00054
00055 uint max_x = MapMaxX();
00056 uint max_y = MapMaxY();
00057 uint min_xy = _settings_game.construction.freeform_edges ? 1 : 0;
00058
00059
00060 for (uint dist = 1; dist < max_dist; dist++) {
00061
00062 y--;
00063
00064
00065 for (DiagDirection dir = DIAGDIR_BEGIN; dir < DIAGDIR_END; dir++) {
00066 static const int8 ddx[DIAGDIR_END] = { -1, 1, 1, -1};
00067 static const int8 ddy[DIAGDIR_END] = { 1, 1, -1, -1};
00068
00069 int dx = ddx[dir];
00070 int dy = ddy[dir];
00071
00072
00073 for (uint a = 0; a < dist; a++) {
00074
00075 if (IsInsideMM(x, min_xy, max_x) && IsInsideMM(y, min_xy, max_y)) {
00076 TileIndex t = TileXY(x, y);
00077 if (IsTileType(t, MP_WATER) == water) return dist;
00078 }
00079 x += dx;
00080 y += dy;
00081 }
00082 }
00083 }
00084
00085 if (!water) {
00086
00087 for (TileIndex t = 0; t < MapSize(); t++) {
00088 if (!IsTileType(t, MP_VOID) && !IsTileType(t, MP_WATER)) return 0x1FF;
00089 }
00090 }
00091
00092 return max_dist;
00093 }
00094
00100 uint32 GetIndustryIDAtOffset(TileIndex tile, const Industry *i)
00101 {
00102 if (!IsTileType(tile, MP_INDUSTRY) || GetIndustryIndex(tile) != i->index) {
00103
00104 return 0xFFFF;
00105 }
00106
00107 IndustryGfx gfx = GetCleanIndustryGfx(tile);
00108 const IndustryTileSpec *indtsp = GetIndustryTileSpec(gfx);
00109 const IndustrySpec *indold = GetIndustrySpec(i->type);
00110
00111 if (gfx < NEW_INDUSTRYOFFSET) {
00112
00113 if (indtsp->grf_prop.override == INVALID_INDUSTRYTILE) {
00114 return 0xFF << 8 | gfx;
00115 }
00116
00117 const IndustryTileSpec *tile_ovr = GetIndustryTileSpec(indtsp->grf_prop.override);
00118
00119 if (tile_ovr->grf_prop.grffile->grfid == indold->grf_prop.grffile->grfid) {
00120 return tile_ovr->grf_prop.local_id;
00121 } else {
00122 return 0xFFFE;
00123 }
00124 }
00125
00126 if (indtsp->grf_prop.spritegroup != NULL) {
00127 if (indtsp->grf_prop.grffile->grfid == indold->grf_prop.grffile->grfid) {
00128 return indtsp->grf_prop.local_id;
00129 } else {
00130 return 0xFFFE;
00131 }
00132 }
00133
00134 return 0xFF << 8 | indtsp->grf_prop.subst_id;
00135 }
00136
00137 static uint32 GetClosestIndustry(TileIndex tile, IndustryType type, const Industry *current)
00138 {
00139 uint32 best_dist = UINT32_MAX;
00140 const Industry *i;
00141 FOR_ALL_INDUSTRIES(i) {
00142 if (i->type != type || i == current) continue;
00143
00144 best_dist = min(best_dist, DistanceManhattan(tile, i->xy));
00145 }
00146
00147 return best_dist;
00148 }
00149
00158 static uint32 GetCountAndDistanceOfClosestInstance(byte param_setID, byte layout_filter, const Industry *current)
00159 {
00160 uint32 GrfID = GetRegister(0x100);
00161 IndustryType ind_index;
00162 uint32 closest_dist = UINT32_MAX;
00163 byte count = 0;
00164
00165
00166 switch (GrfID) {
00167 case 0:
00168 ind_index = param_setID;
00169 break;
00170
00171 case 0xFFFFFFFF:
00172 GrfID = GetIndustrySpec(current->type)->grf_prop.grffile->grfid;
00173
00174
00175 default:
00176 SetBit(param_setID, 7);
00177 ind_index = MapNewGRFIndustryType(param_setID, GrfID);
00178 break;
00179 }
00180
00181 if (layout_filter == 0) {
00182
00183
00184 closest_dist = GetClosestIndustry(current->xy, ind_index, current);
00185 count = GetIndustryTypeCount(ind_index);
00186 } else {
00187
00188
00189 const Industry *i;
00190 FOR_ALL_INDUSTRIES(i) {
00191 if (i->type == ind_index && i != current && i->selected_layout == layout_filter) {
00192 closest_dist = min(closest_dist, DistanceManhattan(current->xy, i->xy));
00193 count++;
00194 }
00195 }
00196 }
00197
00198 return count << 16 | GB(closest_dist, 0, 16);
00199 }
00200
00207 uint32 IndustryGetVariable(const ResolverObject *object, byte variable, byte parameter, bool *available)
00208 {
00209 const Industry *industry = object->u.industry.ind;
00210 TileIndex tile = object->u.industry.tile;
00211 IndustryType type = object->u.industry.type;
00212 const IndustrySpec *indspec = GetIndustrySpec(type);
00213
00214
00215 if (object->u.industry.gfx == INVALID_INDUSTRYTILE && object->scope == VSG_SCOPE_PARENT) {
00216
00217 const Town *t;
00218
00219 if (industry != NULL) {
00220 t = industry->town;
00221 } else if (tile != INVALID_TILE) {
00222 t = ClosestTownFromTile(tile, UINT_MAX);
00223 } else {
00224 *available = false;
00225 return UINT_MAX;
00226 }
00227
00228 return TownGetVariable(variable, parameter, available, t);
00229 }
00230
00231 if (industry == NULL) {
00232
00233 switch (variable) {
00234
00235 case 0x43: return GetClosestWaterDistance(tile, (indspec->behaviour & INDUSTRYBEH_BUILT_ONWATER) == 0);
00236 }
00237
00238 DEBUG(grf, 1, "Unhandled property 0x%X (no available industry) in callback 0x%x", variable, object->callback);
00239
00240 *available = false;
00241 return UINT_MAX;
00242 }
00243
00244 switch (variable) {
00245 case 0x40:
00246 case 0x41:
00247 case 0x42: {
00248 uint16 callback = indspec->callback_flags;
00249 if (HasBit(callback, CBM_IND_PRODUCTION_CARGO_ARRIVAL) || HasBit(callback, CBM_IND_PRODUCTION_256_TICKS)) {
00250 if ((indspec->behaviour & INDUSTRYBEH_PROD_MULTI_HNDLING) != 0) {
00251 return min(industry->incoming_cargo_waiting[variable - 0x40] / industry->prod_level, (uint16)0xFFFF);
00252 } else {
00253 return min(industry->incoming_cargo_waiting[variable - 0x40], (uint16)0xFFFF);
00254 }
00255 } else {
00256 return 0;
00257 }
00258 }
00259
00260
00261 case 0x43: return GetClosestWaterDistance(tile, (indspec->behaviour & INDUSTRYBEH_BUILT_ONWATER) == 0);
00262
00263
00264 case 0x44: return industry->selected_layout;
00265
00266
00267 case 0x45: {
00268 byte colours;
00269 bool is_ai = false;
00270
00271 if (IsValidCompanyID(industry->founder)) {
00272 const Company *c = GetCompany(industry->founder);
00273 const Livery *l = &c->livery[LS_DEFAULT];
00274
00275 is_ai = c->is_ai;
00276 colours = l->colour1 + l->colour2 * 16;
00277 } else {
00278 colours = GB(Random(), 0, 8);
00279 }
00280
00281 return industry->founder | (is_ai ? 0x10000 : 0) | (colours << 24);
00282 }
00283
00284 case 0x46: return industry->construction_date;
00285
00286
00287 case 0x60: return GetIndustryIDAtOffset(GetNearbyTile(parameter, industry->xy), industry);
00288
00289
00290 case 0x61:
00291 tile = GetNearbyTile(parameter, tile);
00292 return (IsTileType(tile, MP_INDUSTRY) && GetIndustryByTile(tile) == industry) ? GetIndustryRandomBits(tile) : 0;
00293
00294
00295 case 0x62: return GetNearbyIndustryTileInformation(parameter, tile, INVALID_INDUSTRY);
00296
00297
00298 case 0x63:
00299 tile = GetNearbyTile(parameter, tile);
00300 if (IsTileType(tile, MP_INDUSTRY) && GetIndustryByTile(tile) == industry) {
00301 return GetIndustryAnimationState(tile);
00302 }
00303 return 0xFFFFFFFF;
00304
00305
00306 case 0x64: return GetClosestIndustry(tile, MapNewGRFIndustryType(parameter, indspec->grf_prop.grffile->grfid), industry);
00307
00308 case 0x65: return GetTownRadiusGroup(industry->town, tile) << 16 | min(DistanceManhattan(tile, industry->town->xy), 0xFFFF);
00309
00310 case 0x66: return GetTownRadiusGroup(industry->town, tile) << 16 | min(DistanceSquare(tile, industry->town->xy), 0xFFFF);
00311
00312
00313
00314 case 0x67:
00315 case 0x68: return GetCountAndDistanceOfClosestInstance(parameter, variable == 0x68 ? GB(GetRegister(0x101), 0, 8) : 0, industry);
00316
00317
00318 case 0x7C: return industry->psa.Get(parameter);
00319
00320
00321 case 0x80: return industry->xy;
00322 case 0x81: return GB(industry->xy, 8, 8);
00323
00324 case 0x82: return industry->town->index;
00325 case 0x83:
00326 case 0x84:
00327 case 0x85: DEBUG(grf, 0, "NewGRFs shouldn't be doing pointer magic"); break;
00328 case 0x86: return industry->width;
00329 case 0x87: return industry->height;
00330
00331 case 0x88:
00332 case 0x89: return industry->produced_cargo[variable - 0x88];
00333 case 0x8A: return industry->produced_cargo_waiting[0];
00334 case 0x8B: return GB(industry->produced_cargo_waiting[0], 8, 8);
00335 case 0x8C: return industry->produced_cargo_waiting[1];
00336 case 0x8D: return GB(industry->produced_cargo_waiting[1], 8, 8);
00337 case 0x8E:
00338 case 0x8F: return industry->production_rate[variable - 0x8E];
00339 case 0x90:
00340 case 0x91:
00341 case 0x92: return industry->accepts_cargo[variable - 0x90];
00342 case 0x93: return industry->prod_level;
00343
00344 case 0x94: return industry->this_month_production[0];
00345 case 0x95: return GB(industry->this_month_production[0], 8, 8);
00346 case 0x96: return industry->this_month_production[1];
00347 case 0x97: return GB(industry->this_month_production[1], 8, 8);
00348
00349 case 0x98: return industry->this_month_transported[0];
00350 case 0x99: return GB(industry->this_month_transported[0], 8, 8);
00351 case 0x9A: return industry->this_month_transported[1];
00352 case 0x9B: return GB(industry->this_month_transported[0], 8, 8);
00353
00354 case 0x9C:
00355 case 0x9D: return industry->last_month_pct_transported[variable - 0x9C];
00356
00357 case 0x9E: return industry->last_month_production[0];
00358 case 0x9F: return GB(industry->last_month_production[0], 8, 8);
00359 case 0xA0: return industry->last_month_production[1];
00360 case 0xA1: return GB(industry->last_month_production[1], 8, 8);
00361
00362 case 0xA2: return industry->last_month_transported[0];
00363 case 0xA3: return GB(industry->last_month_transported[0], 8, 8);
00364 case 0xA4: return industry->last_month_transported[1];
00365 case 0xA5: return GB(industry->last_month_transported[0], 8, 8);
00366
00367 case 0xA6: return industry->type;
00368 case 0xA7: return industry->founder;
00369 case 0xA8: return industry->random_colour;
00370 case 0xA9: return Clamp(industry->last_prod_year - ORIGINAL_BASE_YEAR, 0, 255);
00371 case 0xAA: return industry->counter;
00372 case 0xAB: return GB(industry->counter, 8, 8);
00373 case 0xAC: return industry->was_cargo_delivered;
00374
00375 case 0xB0: return Clamp(industry->construction_date - DAYS_TILL_ORIGINAL_BASE_YEAR, 0, 65535);
00376 case 0xB3: return industry->construction_type;
00377 case 0xB4: return Clamp(industry->last_cargo_accepted_at - DAYS_TILL_ORIGINAL_BASE_YEAR, 0, 65535);
00378 }
00379
00380 DEBUG(grf, 1, "Unhandled industry property 0x%X", variable);
00381
00382 *available = false;
00383 return UINT_MAX;
00384 }
00385
00386 static const SpriteGroup *IndustryResolveReal(const ResolverObject *object, const SpriteGroup *group)
00387 {
00388
00389 return NULL;
00390 }
00391
00392 static uint32 IndustryGetRandomBits(const ResolverObject *object)
00393 {
00394 return object->u.industry.ind == NULL ? 0 : object->u.industry.ind->random;
00395 }
00396
00397 static uint32 IndustryGetTriggers(const ResolverObject *object)
00398 {
00399 return object->u.industry.ind == NULL ? 0 : object->u.industry.ind->random_triggers;
00400 }
00401
00402 static void IndustrySetTriggers(const ResolverObject *object, int triggers)
00403 {
00404 if (object->u.industry.ind == NULL) return;
00405 object->u.industry.ind->random_triggers = triggers;
00406 }
00407
00408 static void NewIndustryResolver(ResolverObject *res, TileIndex tile, Industry *indus, IndustryType type)
00409 {
00410 res->GetRandomBits = IndustryGetRandomBits;
00411 res->GetTriggers = IndustryGetTriggers;
00412 res->SetTriggers = IndustrySetTriggers;
00413 res->GetVariable = IndustryGetVariable;
00414 res->ResolveReal = IndustryResolveReal;
00415
00416 res->psa = &indus->psa;
00417 res->u.industry.tile = tile;
00418 res->u.industry.ind = indus;
00419 res->u.industry.gfx = INVALID_INDUSTRYTILE;
00420 res->u.industry.type = type;
00421
00422 res->callback = CBID_NO_CALLBACK;
00423 res->callback_param1 = 0;
00424 res->callback_param2 = 0;
00425 res->last_value = 0;
00426 res->trigger = 0;
00427 res->reseed = 0;
00428 res->count = 0;
00429
00430 const IndustrySpec *indspec = GetIndustrySpec(type);
00431 res->grffile = (indspec != NULL ? indspec->grf_prop.grffile : NULL);
00432 }
00433
00434 uint16 GetIndustryCallback(CallbackID callback, uint32 param1, uint32 param2, Industry *industry, IndustryType type, TileIndex tile)
00435 {
00436 ResolverObject object;
00437 const SpriteGroup *group;
00438
00439 NewIndustryResolver(&object, tile, industry, type);
00440 object.callback = callback;
00441 object.callback_param1 = param1;
00442 object.callback_param2 = param2;
00443
00444 group = Resolve(GetIndustrySpec(type)->grf_prop.spritegroup, &object);
00445 if (group == NULL || group->type != SGT_CALLBACK) return CALLBACK_FAILED;
00446
00447 return group->g.callback.result;
00448 }
00449
00450 uint32 IndustryLocationGetVariable(const ResolverObject *object, byte variable, byte parameter, bool *available)
00451 {
00452 const Industry *industry = object->u.industry.ind;
00453 TileIndex tile = object->u.industry.tile;
00454
00455 if (object->scope == VSG_SCOPE_PARENT) {
00456 return TownGetVariable(variable, parameter, available, industry->town);
00457 }
00458
00459 switch (variable) {
00460 case 0x80: return tile;
00461 case 0x81: return GB(tile, 8, 8);
00462
00463
00464 case 0x82: return industry->town->index;
00465 case 0x83:
00466 case 0x84:
00467 case 0x85: DEBUG(grf, 0, "NewGRFs shouldn't be doing pointer magic"); break;
00468
00469
00470 case 0x86: return industry->selected_layout;
00471
00472
00473 case 0x87: return GetTerrainType(tile);
00474
00475
00476 case 0x88: return GetTownRadiusGroup(industry->town, tile);
00477
00478
00479 case 0x89: return min(DistanceManhattan(industry->town->xy, tile), 255);
00480
00481
00482 case 0x8A: return GetTileZ(tile);
00483
00484
00485 case 0x8B: return GetClosestWaterDistance(tile, (GetIndustrySpec(industry->type)->behaviour & INDUSTRYBEH_BUILT_ONWATER) == 0);
00486
00487
00488 case 0x8D: return min(DistanceSquare(industry->town->xy, tile), 65535);
00489
00490
00491 case 0x8F: return _industry_creation_random_bits;
00492 }
00493
00494
00495 return IndustryGetVariable(object, variable, parameter, available);
00496 }
00497
00498 bool CheckIfCallBackAllowsCreation(TileIndex tile, IndustryType type, uint itspec_index, uint32 seed)
00499 {
00500 const IndustrySpec *indspec = GetIndustrySpec(type);
00501
00502 ResolverObject object;
00503 const SpriteGroup *group;
00504
00505 Industry ind;
00506 ind.index = INVALID_INDUSTRY;
00507 ind.xy = tile;
00508 ind.width = 0;
00509 ind.type = type;
00510 ind.selected_layout = itspec_index;
00511 ind.town = ClosestTownFromTile(tile, UINT_MAX);
00512
00513 NewIndustryResolver(&object, tile, &ind, type);
00514 object.GetVariable = IndustryLocationGetVariable;
00515 object.callback = CBID_INDUSTRY_LOCATION;
00516 _industry_creation_random_bits = seed;
00517
00518 group = Resolve(GetIndustrySpec(type)->grf_prop.spritegroup, &object);
00519
00520
00521
00522 if (group == NULL || group->type != SGT_CALLBACK || group->g.callback.result == 0x400) return true;
00523
00524
00525 SwitchToErrorRefStack();
00526 PrepareTextRefStackUsage(4);
00527 SwitchToNormalRefStack();
00528
00529 switch (group->g.callback.result) {
00530 case 0x401: _error_message = STR_0239_SITE_UNSUITABLE; break;
00531 case 0x402: _error_message = STR_0317_CAN_ONLY_BE_BUILT_IN_RAINFOREST; break;
00532 case 0x403: _error_message = STR_0318_CAN_ONLY_BE_BUILT_IN_DESERT; break;
00533 default: _error_message = GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 + group->g.callback.result); break;
00534 }
00535
00536 return false;
00537 }
00538
00539 bool CheckIfCallBackAllowsAvailability(IndustryType type, IndustryAvailabilityCallType creation_type)
00540 {
00541 const IndustrySpec *indspec = GetIndustrySpec(type);
00542
00543 if (HasBit(indspec->callback_flags, CBM_IND_AVAILABLE)) {
00544 uint16 res = GetIndustryCallback(CBID_INDUSTRY_AVAILABLE, 0, creation_type, NULL, type, INVALID_TILE);
00545 if (res != CALLBACK_FAILED) {
00546 return (res == 0);
00547 }
00548 }
00549 return true;
00550 }
00551
00552 static int32 DerefIndProd(uint field, bool use_register)
00553 {
00554 return use_register ? (int32)GetRegister(field) : field;
00555 }
00556
00562 void IndustryProductionCallback(Industry *ind, int reason)
00563 {
00564 const IndustrySpec *spec = GetIndustrySpec(ind->type);
00565 ResolverObject object;
00566 NewIndustryResolver(&object, ind->xy, ind, ind->type);
00567 if ((spec->behaviour & INDUSTRYBEH_PRODCALLBACK_RANDOM) != 0) object.callback_param1 = Random();
00568 int multiplier = 1;
00569 if ((spec->behaviour & INDUSTRYBEH_PROD_MULTI_HNDLING) != 0) multiplier = ind->prod_level;
00570 object.callback_param2 = reason;
00571
00572 for (uint loop = 0;; loop++) {
00573 SB(object.callback_param2, 8, 16, loop);
00574 const SpriteGroup *group = Resolve(spec->grf_prop.spritegroup, &object);
00575 if (group == NULL || group->type != SGT_INDUSTRY_PRODUCTION) break;
00576
00577 bool deref = (group->g.indprod.version == 1);
00578
00579 for (uint i = 0; i < 3; i++) {
00580 ind->incoming_cargo_waiting[i] = Clamp(ind->incoming_cargo_waiting[i] - DerefIndProd(group->g.indprod.substract_input[i], deref) * multiplier, 0, 0xFFFF);
00581 }
00582 for (uint i = 0; i < 2; i++) {
00583 ind->produced_cargo_waiting[i] = Clamp(ind->produced_cargo_waiting[i] + max(DerefIndProd(group->g.indprod.add_output[i], deref), 0) * multiplier, 0, 0xFFFF);
00584 }
00585
00586 int32 again = DerefIndProd(group->g.indprod.again, deref);
00587 if (again == 0) break;
00588
00589 SB(object.callback_param2, 24, 8, again);
00590 }
00591
00592 InvalidateWindow(WC_INDUSTRY_VIEW, ind->index);
00593 }