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