00001
00002
00005 #include "stdafx.h"
00006 #include "openttd.h"
00007 #include "debug.h"
00008 #include "variables.h"
00009 #include "landscape.h"
00010 #include "industry.h"
00011 #include "industry_map.h"
00012 #include "newgrf.h"
00013 #include "newgrf_callbacks.h"
00014 #include "newgrf_spritegroup.h"
00015 #include "newgrf_industries.h"
00016 #include "newgrf_industrytiles.h"
00017 #include "newgrf_commons.h"
00018 #include "newgrf_text.h"
00019 #include "newgrf_town.h"
00020 #include "window_func.h"
00021 #include "town.h"
00022 #include "player_func.h"
00023 #include "player_base.h"
00024 #include "command_func.h"
00025
00026 #include "table/strings.h"
00027
00028 static uint32 _industry_creation_random_bits;
00029
00030
00031
00032
00033 IndustryOverrideManager _industry_mngr(NEW_INDUSTRYOFFSET, NUM_INDUSTRYTYPES, INVALID_INDUSTRYTYPE);
00034 IndustryTileOverrideManager _industile_mngr(NEW_INDUSTRYTILEOFFSET, NUM_INDUSTRYTILES, INVALID_INDUSTRYTILE);
00035
00036 IndustryType MapNewGRFIndustryType(IndustryType grf_type, uint32 grf_id)
00037 {
00038 if (grf_type == IT_INVALID) return IT_INVALID;
00039 if (!HasBit(grf_type, 7)) return GB(grf_type, 0, 6);
00040
00041 return _industry_mngr.GetID(GB(grf_type, 0, 6), grf_id);
00042 }
00043
00044 static uint32 GetGRFParameter(IndustryType ind_id, byte parameter)
00045 {
00046 const IndustrySpec *indspec = GetIndustrySpec(ind_id);
00047 const GRFFile *file = indspec->grf_prop.grffile;
00048
00049 if (parameter >= file->param_end) return 0;
00050 return file->param[parameter];
00051 }
00052
00059 static uint GetClosestWaterDistance(TileIndex tile, bool water)
00060 {
00061 TileIndex t;
00062 int best_dist;
00063 for (t = 0; t < MapSize(); t++) {
00064 if (IsTileType(t, MP_WATER) == water) break;
00065 }
00066 if (t == MapSize() && !water) return 0x200;
00067 best_dist = DistanceManhattan(tile, t);
00068
00069 for (; t < MapSize(); t++) {
00070 int dist = DistanceManhattan(tile, t);
00071 if (dist < best_dist) {
00072 if (IsTileType(t, MP_WATER) == water) best_dist = dist;
00073 } else {
00074
00075
00076 if ((int)TileY(t) - (int)TileY(tile) > best_dist) break;
00077 if ((int)TileX(t) - (int)TileX(tile) > best_dist) {
00078
00079
00080 t |= MapMaxX();
00081 continue;
00082 } else if (TileX(tile) < TileX(t)) {
00083
00084
00085 t += max(best_dist - dist, 0);
00086 continue;
00087 }
00088 }
00089 }
00090
00091 return min(best_dist, water ? 0x7F : 0x1FF);
00092 }
00093
00099 uint32 GetIndustryIDAtOffset(TileIndex tile, const Industry *i)
00100 {
00101 if (!IsTileType(tile, MP_INDUSTRY) || GetIndustryIndex(tile) != i->index) {
00102
00103 return 0xFFFF;
00104 }
00105
00106 IndustryGfx gfx = GetCleanIndustryGfx(tile);
00107 const IndustryTileSpec *indtsp = GetIndustryTileSpec(gfx);
00108 const IndustrySpec *indold = GetIndustrySpec(i->type);
00109
00110 if (gfx < NEW_INDUSTRYOFFSET) {
00111
00112 if (indtsp->grf_prop.override == INVALID_INDUSTRYTILE) {
00113 return 0xFF << 8 | gfx;
00114 }
00115
00116 const IndustryTileSpec *tile_ovr = GetIndustryTileSpec(indtsp->grf_prop.override);
00117
00118 if (tile_ovr->grf_prop.grffile->grfid == indold->grf_prop.grffile->grfid) {
00119 return tile_ovr->grf_prop.local_id;
00120 } else {
00121 return 0xFFFE;
00122 }
00123 }
00124
00125 if (indtsp->grf_prop.spritegroup != NULL) {
00126 if (indtsp->grf_prop.grffile->grfid == indold->grf_prop.grffile->grfid) {
00127 return indtsp->grf_prop.local_id;
00128 } else {
00129 return 0xFFFE;
00130 }
00131 }
00132
00133 return 0xFF << 8 | indtsp->grf_prop.subst_id;
00134 }
00135
00136 static uint32 GetClosestIndustry(TileIndex tile, IndustryType type, const Industry *current)
00137 {
00138 uint32 best_dist = MAX_UVALUE(uint32);
00139 const Industry *i;
00140 FOR_ALL_INDUSTRIES(i) {
00141 if (i->type != type || i == current) continue;
00142
00143 best_dist = min(best_dist, DistanceManhattan(tile, i->xy));
00144 }
00145
00146 return best_dist;
00147 }
00148
00157 static uint32 GetCountAndDistanceOfClosestInstance(byte param_setID, byte layout_filter, const Industry *current)
00158 {
00159 uint32 GrfID = GetRegister(0x100);
00160 IndustryType ind_index;
00161 uint32 closest_dist = MAX_UVALUE(uint32);
00162 byte count = 0;
00163
00164
00165 switch (GrfID) {
00166 case 0:
00167 ind_index = param_setID;
00168 break;
00169
00170 case 0xFFFFFFFF:
00171 GrfID = GetIndustrySpec(current->type)->grf_prop.grffile->grfid;
00172
00173
00174 default:
00175 SetBit(param_setID, 7);
00176 ind_index = MapNewGRFIndustryType(param_setID, GrfID);
00177 break;
00178 }
00179
00180 if (layout_filter == 0) {
00181
00182
00183 closest_dist = GetClosestIndustry(current->xy, ind_index, current);
00184 count = GetIndustryTypeCount(ind_index);
00185 } else {
00186
00187
00188 const Industry *i;
00189 FOR_ALL_INDUSTRIES(i) {
00190 if (i->type == ind_index && i != current && i->selected_layout == layout_filter) {
00191 closest_dist = min(closest_dist, DistanceManhattan(current->xy, i->xy));
00192 count++;
00193 }
00194 }
00195 }
00196
00197 return count << 16 | GB(closest_dist, 0, 16);
00198 }
00199
00206 uint32 IndustryGetVariable(const ResolverObject *object, byte variable, byte parameter, bool *available)
00207 {
00208 const Industry *industry = object->u.industry.ind;
00209 TileIndex tile = object->u.industry.tile;
00210 IndustryType type = object->u.industry.type;
00211 const IndustrySpec *indspec = GetIndustrySpec(type);
00212
00213
00214 if (object->u.industry.gfx == INVALID_INDUSTRYTILE && object->scope == VSG_SCOPE_PARENT) {
00215
00216 const Town *t;
00217
00218 if (industry != NULL) {
00219 t = industry->town;
00220 } else if (tile != INVALID_TILE) {
00221 t = ClosestTownFromTile(tile, UINT_MAX);
00222 } else {
00223 *available = false;
00224 return UINT_MAX;
00225 }
00226
00227 return TownGetVariable(variable, parameter, available, t);
00228 }
00229
00230 if (industry == NULL) {
00231
00232 switch (variable) {
00233
00234 case 0x7F: return GetGRFParameter(type, parameter);
00235
00236 case 0x43: return GetClosestWaterDistance(tile, (indspec->behaviour & INDUSTRYBEH_BUILT_ONWATER) == 0);
00237 }
00238
00239 DEBUG(grf, 1, "Unhandled property 0x%X (no available industry) in callback 0x%x", variable, object->callback);
00240
00241 *available = false;
00242 return UINT_MAX;
00243 }
00244
00245 switch (variable) {
00246 case 0x40:
00247 case 0x41:
00248 case 0x42: {
00249 uint16 callback = indspec->callback_flags;
00250 if (HasBit(callback, CBM_IND_PRODUCTION_CARGO_ARRIVAL) || HasBit(callback, CBM_IND_PRODUCTION_256_TICKS)) {
00251 if ((indspec->behaviour & INDUSTRYBEH_PROD_MULTI_HNDLING) != 0) {
00252 return min(industry->incoming_cargo_waiting[variable - 0x40] / industry->prod_level, (uint16)0xFFFF);
00253 } else {
00254 return min(industry->incoming_cargo_waiting[variable - 0x40], (uint16)0xFFFF);
00255 }
00256 } else {
00257 return 0;
00258 }
00259 }
00260
00261
00262 case 0x43: return GetClosestWaterDistance(tile, (indspec->behaviour & INDUSTRYBEH_BUILT_ONWATER) == 0);
00263
00264
00265 case 0x44: return industry->selected_layout;
00266
00267
00268 case 0x45: {
00269 byte colours;
00270 bool is_ai = false;
00271
00272 if (IsValidPlayer(industry->founder)) {
00273 const Player *p = GetPlayer(industry->founder);
00274 const Livery *l = &p->livery[LS_DEFAULT];
00275
00276 is_ai = p->is_ai;
00277 colours = l->colour1 + l->colour2 * 16;
00278 } else {
00279 colours = GB(Random(), 0, 8);
00280 }
00281
00282 return industry->founder | (is_ai ? 0x10000 : 0) | (colours << 24);
00283 }
00284
00285
00286 case 0x60: return GetIndustryIDAtOffset(GetNearbyTile(parameter, industry->xy), industry);
00287
00288
00289 case 0x61:
00290 tile = GetNearbyTile(parameter, tile);
00291 return (IsTileType(tile, MP_INDUSTRY) && GetIndustryByTile(tile) == industry) ? GetIndustryRandomBits(tile) : 0;
00292
00293
00294 case 0x62: return GetNearbyIndustryTileInformation(parameter, tile, INVALID_INDUSTRY);
00295
00296
00297 case 0x63:
00298 tile = GetNearbyTile(parameter, tile);
00299 if (IsTileType(tile, MP_INDUSTRY) && GetIndustryByTile(tile) == industry) {
00300 return GetIndustryAnimationState(tile);
00301 }
00302 return 0xFFFFFFFF;
00303
00304
00305 case 0x64: return GetClosestIndustry(tile, MapNewGRFIndustryType(parameter, indspec->grf_prop.grffile->grfid), industry);
00306
00307 case 0x65: return GetTownRadiusGroup(industry->town, tile) << 16 | min(DistanceManhattan(tile, industry->town->xy), 0xFFFF);
00308
00309 case 0x66: return GetTownRadiusGroup(industry->town, tile) << 16 | min(DistanceSquare(tile, industry->town->xy), 0xFFFF);
00310
00311
00312
00313 case 0x67:
00314 case 0x68: return GetCountAndDistanceOfClosestInstance(parameter, variable == 0x68 ? GB(GetRegister(0x101), 0, 8) : 0, industry);
00315
00316
00317 case 0x7C: return industry->psa.Get(parameter);
00318
00319
00320 case 0x7F: return GetGRFParameter(type, 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_color;
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 (uint32)-1;
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 }
00431
00432 uint16 GetIndustryCallback(CallbackID callback, uint32 param1, uint32 param2, Industry *industry, IndustryType type, TileIndex tile)
00433 {
00434 ResolverObject object;
00435 const SpriteGroup *group;
00436
00437 NewIndustryResolver(&object, tile, industry, type);
00438 object.callback = callback;
00439 object.callback_param1 = param1;
00440 object.callback_param2 = param2;
00441
00442 group = Resolve(GetIndustrySpec(type)->grf_prop.spritegroup, &object);
00443 if (group == NULL || group->type != SGT_CALLBACK) return CALLBACK_FAILED;
00444
00445 return group->g.callback.result;
00446 }
00447
00448 uint32 IndustryLocationGetVariable(const ResolverObject *object, byte variable, byte parameter, bool *available)
00449 {
00450 const Industry *industry = object->u.industry.ind;
00451 TileIndex tile = object->u.industry.tile;
00452
00453 if (object->scope == VSG_SCOPE_PARENT) {
00454 return TownGetVariable(variable, parameter, available, industry->town);
00455 }
00456
00457 switch (variable) {
00458 case 0x80: return tile;
00459 case 0x81: return GB(tile, 8, 8);
00460
00461
00462 case 0x82: return industry->town->index;
00463 case 0x83:
00464 case 0x84:
00465 case 0x85: DEBUG(grf, 0, "NewGRFs shouldn't be doing pointer magic"); break;
00466
00467
00468 case 0x86: return industry->selected_layout;
00469
00470
00471 case 0x87: return GetTerrainType(tile);
00472
00473
00474 case 0x88: return GetTownRadiusGroup(industry->town, tile);
00475
00476
00477 case 0x89: return min(DistanceManhattan(industry->town->xy, tile), 255);
00478
00479
00480 case 0x8A: return GetTileZ(tile);
00481
00482
00483 case 0x8B: return GetClosestWaterDistance(tile, (GetIndustrySpec(industry->type)->behaviour & INDUSTRYBEH_BUILT_ONWATER) == 0);
00484
00485
00486 case 0x8D: return min(DistanceSquare(industry->town->xy, tile), 65535);
00487
00488
00489 case 0x8F: return _industry_creation_random_bits;
00490 }
00491
00492
00493 return IndustryGetVariable(object, variable, parameter, available);
00494 }
00495
00496 bool CheckIfCallBackAllowsCreation(TileIndex tile, IndustryType type, uint itspec_index, uint32 seed)
00497 {
00498 const IndustrySpec *indspec = GetIndustrySpec(type);
00499
00500 ResolverObject object;
00501 const SpriteGroup *group;
00502
00503 Industry ind;
00504 ind.index = INVALID_INDUSTRY;
00505 ind.xy = tile;
00506 ind.width = 0;
00507 ind.type = type;
00508 ind.selected_layout = itspec_index;
00509 ind.town = ClosestTownFromTile(tile, (uint)-1);
00510
00511 NewIndustryResolver(&object, tile, &ind, type);
00512 object.GetVariable = IndustryLocationGetVariable;
00513 object.callback = CBID_INDUSTRY_LOCATION;
00514 _industry_creation_random_bits = seed;
00515
00516 group = Resolve(GetIndustrySpec(type)->grf_prop.spritegroup, &object);
00517
00518
00519
00520 if (group == NULL || group->type != SGT_CALLBACK) return true;
00521
00522
00523 SwitchToErrorRefStack();
00524 PrepareTextRefStackUsage(4);
00525 SwitchToNormalRefStack();
00526
00527 switch (group->g.callback.result) {
00528 case 0x400: return true;
00529 case 0x401: _error_message = STR_0239_SITE_UNSUITABLE; break;
00530 case 0x402: _error_message = STR_0317_CAN_ONLY_BE_BUILT_IN_RAINFOREST; break;
00531 case 0x403: _error_message = STR_0318_CAN_ONLY_BE_BUILT_IN_DESERT; break;
00532 default: _error_message = GetGRFStringID(indspec->grf_prop.grffile->grfid, 0xD000 + group->g.callback.result); break;
00533 }
00534
00535 return false;
00536 }
00537
00538 bool CheckIfCallBackAllowsAvailability(IndustryType type, IndustryAvailabilityCallType creation_type)
00539 {
00540 const IndustrySpec *indspec = GetIndustrySpec(type);
00541
00542 if (HasBit(indspec->callback_flags, CBM_IND_AVAILABLE)) {
00543 uint16 res = GetIndustryCallback(CBID_INDUSTRY_AVAILABLE, 0, creation_type, NULL, type, INVALID_TILE);
00544 if (res != CALLBACK_FAILED) {
00545 return (res == 0);
00546 }
00547 }
00548 return true;
00549 }
00550
00551 static int32 DerefIndProd(uint field, bool use_register)
00552 {
00553 return use_register ? (int32)GetRegister(field) : field;
00554 }
00555
00561 void IndustryProductionCallback(Industry *ind, int reason)
00562 {
00563 const IndustrySpec *spec = GetIndustrySpec(ind->type);
00564 ResolverObject object;
00565 NewIndustryResolver(&object, ind->xy, ind, ind->type);
00566 if ((spec->behaviour & INDUSTRYBEH_PRODCALLBACK_RANDOM) != 0) object.callback_param1 = Random();
00567 int multiplier = 1;
00568 if ((spec->behaviour & INDUSTRYBEH_PROD_MULTI_HNDLING) != 0) multiplier = ind->prod_level;
00569 object.callback_param2 = reason;
00570
00571 for (uint loop = 0;; loop++) {
00572 SB(object.callback_param2, 8, 16, loop);
00573 const SpriteGroup *group = Resolve(spec->grf_prop.spritegroup, &object);
00574 if (group == NULL || group->type != SGT_INDUSTRY_PRODUCTION) break;
00575
00576 bool deref = (group->g.indprod.version == 1);
00577
00578 for (uint i = 0; i < 3; i++) {
00579 ind->incoming_cargo_waiting[i] = Clamp(ind->incoming_cargo_waiting[i] - DerefIndProd(group->g.indprod.substract_input[i], deref) * multiplier, 0, 0xFFFF);
00580 }
00581 for (uint i = 0; i < 2; i++) {
00582 ind->produced_cargo_waiting[i] = Clamp(ind->produced_cargo_waiting[i] + max(DerefIndProd(group->g.indprod.add_output[i], deref), 0) * multiplier, 0, 0xFFFF);
00583 }
00584
00585 int32 again = DerefIndProd(group->g.indprod.again, deref);
00586 if (again == 0) break;
00587
00588 SB(object.callback_param2, 24, 8, again);
00589 }
00590
00591 InvalidateWindow(WC_INDUSTRY_VIEW, ind->index);
00592 }