00001
00002
00003
00004
00005
00006
00007
00008
00009
00012 #include "stdafx.h"
00013 #include "openttd.h"
00014 #include "variables.h"
00015 #include "debug.h"
00016 #include "viewport_func.h"
00017 #include "landscape.h"
00018 #include "newgrf.h"
00019 #include "core/random_func.hpp"
00020 #include "newgrf_commons.h"
00021 #include "newgrf_industries.h"
00022 #include "newgrf_industrytiles.h"
00023 #include "newgrf_sound.h"
00024 #include "newgrf_text.h"
00025 #include "industry.h"
00026 #include "sprite.h"
00027 #include "transparency.h"
00028 #include "functions.h"
00029 #include "town.h"
00030 #include "command_func.h"
00031 #include "animated_tile_func.h"
00032 #include "water.h"
00033
00034 #include "table/strings.h"
00035
00043 uint32 GetNearbyIndustryTileInformation(byte parameter, TileIndex tile, IndustryID index)
00044 {
00045 if (parameter != 0) tile = GetNearbyTile(parameter, tile);
00046 bool is_same_industry = (IsTileType(tile, MP_INDUSTRY) && GetIndustryIndex(tile) == index);
00047
00048 return GetNearbyTileInformation(tile) | (is_same_industry ? 1 : 0) << 8;
00049 }
00050
00061 static uint32 GetRelativePosition(TileIndex tile, TileIndex ind_tile)
00062 {
00063 byte x = TileX(tile) - TileX(ind_tile);
00064 byte y = TileY(tile) - TileY(ind_tile);
00065
00066 return ((y & 0xF) << 20) | ((x & 0xF) << 16) | (y << 8) | x;
00067 }
00068
00069 static uint32 IndustryTileGetVariable(const ResolverObject *object, byte variable, byte parameter, bool *available)
00070 {
00071 const Industry *inds = object->u.industry.ind;
00072 TileIndex tile = object->u.industry.tile;
00073
00074 if (object->scope == VSG_SCOPE_PARENT) {
00075 return IndustryGetVariable(object, variable, parameter, available);
00076 }
00077
00078 switch (variable) {
00079
00080 case 0x40: return (IsTileType(tile, MP_INDUSTRY)) ? GetIndustryConstructionStage(tile) : 0;
00081
00082
00083 case 0x41: return GetTerrainType(tile);
00084
00085
00086 case 0x42: return GetTownRadiusGroup(ClosestTownFromTile(tile, UINT_MAX), tile);
00087
00088
00089 case 0x43: return GetRelativePosition(tile, inds->xy);
00090
00091
00092 case 0x44: return (IsTileType(tile, MP_INDUSTRY)) ? GetIndustryAnimationState(tile) : 0;
00093
00094
00095 case 0x60: return GetNearbyIndustryTileInformation(parameter, tile, inds == NULL ? (IndustryID)INVALID_INDUSTRY : inds->index);
00096
00097
00098 case 0x61:
00099 tile = GetNearbyTile(parameter, tile);
00100 if (IsTileType(tile, MP_INDUSTRY) && Industry::GetByTile(tile) == inds) {
00101 return GetIndustryAnimationState(tile);
00102 }
00103 return UINT_MAX;
00104
00105
00106 case 0x62: return GetIndustryIDAtOffset(GetNearbyTile(parameter, tile), inds);
00107 }
00108
00109 DEBUG(grf, 1, "Unhandled industry tile property 0x%X", variable);
00110
00111 *available = false;
00112 return UINT_MAX;
00113 }
00114
00115 static const SpriteGroup *IndustryTileResolveReal(const ResolverObject *object, const RealSpriteGroup *group)
00116 {
00117
00118 return NULL;
00119 }
00120
00121 static uint32 IndustryTileGetRandomBits(const ResolverObject *object)
00122 {
00123 const TileIndex tile = object->u.industry.tile;
00124 if (tile == INVALID_TILE || !IsTileType(tile, MP_INDUSTRY)) return 0;
00125 return (object->scope == VSG_SCOPE_SELF) ? GetIndustryRandomBits(tile) : Industry::GetByTile(tile)->random;
00126 }
00127
00128 static uint32 IndustryTileGetTriggers(const ResolverObject *object)
00129 {
00130 const TileIndex tile = object->u.industry.tile;
00131 if (tile == INVALID_TILE || !IsTileType(tile, MP_INDUSTRY)) return 0;
00132 return (object->scope == VSG_SCOPE_SELF) ? GetIndustryTriggers(tile) : Industry::GetByTile(tile)->random_triggers;
00133 }
00134
00135 static void IndustryTileSetTriggers(const ResolverObject *object, int triggers)
00136 {
00137 const TileIndex tile = object->u.industry.tile;
00138 if (tile == INVALID_TILE || !IsTileType(tile, MP_INDUSTRY)) return;
00139
00140 if (object->scope == VSG_SCOPE_SELF) {
00141 SetIndustryTriggers(tile, triggers);
00142 } else {
00143 Industry::GetByTile(tile)->random_triggers = triggers;
00144 }
00145 }
00146
00147 static void NewIndustryTileResolver(ResolverObject *res, IndustryGfx gfx, TileIndex tile, Industry *indus)
00148 {
00149 res->GetRandomBits = IndustryTileGetRandomBits;
00150 res->GetTriggers = IndustryTileGetTriggers;
00151 res->SetTriggers = IndustryTileSetTriggers;
00152 res->GetVariable = IndustryTileGetVariable;
00153 res->ResolveReal = IndustryTileResolveReal;
00154
00155 res->psa = &indus->psa;
00156 res->u.industry.tile = tile;
00157 res->u.industry.ind = indus;
00158 res->u.industry.gfx = gfx;
00159 res->u.industry.type = indus->type;
00160
00161 res->callback = CBID_NO_CALLBACK;
00162 res->callback_param1 = 0;
00163 res->callback_param2 = 0;
00164 res->last_value = 0;
00165 res->trigger = 0;
00166 res->reseed = 0;
00167 res->count = 0;
00168
00169 const IndustryTileSpec *its = GetIndustryTileSpec(gfx);
00170 res->grffile = (its != NULL ? its->grf_prop.grffile : NULL);
00171 }
00172
00173 static void IndustryDrawTileLayout(const TileInfo *ti, const TileLayoutSpriteGroup *group, byte rnd_colour, byte stage, IndustryGfx gfx)
00174 {
00175 const DrawTileSprites *dts = group->dts;
00176 const DrawTileSeqStruct *dtss;
00177
00178 SpriteID image = dts->ground.sprite;
00179 SpriteID pal = dts->ground.pal;
00180
00181 if (IS_CUSTOM_SPRITE(image)) image += stage;
00182
00183 if (GB(image, 0, SPRITE_WIDTH) != 0) {
00184
00185
00186 if (image == SPR_FLAT_WATER_TILE && IsIndustryTileOnWater(ti->tile)) {
00187 DrawWaterClassGround(ti);
00188 } else {
00189 DrawGroundSprite(image, GroundSpritePaletteTransform(image, pal, GENERAL_SPRITE_COLOUR(rnd_colour)));
00190 }
00191 }
00192
00193 foreach_draw_tile_seq(dtss, dts->seq) {
00194 if (GB(dtss->image.sprite, 0, SPRITE_WIDTH) == 0) continue;
00195
00196 image = dtss->image.sprite;
00197 pal = dtss->image.pal;
00198
00199
00200 if (IsInvisibilitySet(TO_INDUSTRIES) && !HasBit(image, SPRITE_MODIFIER_OPAQUE)) return;
00201
00202 if (IS_CUSTOM_SPRITE(image)) image += stage;
00203
00204 pal = SpriteLayoutPaletteTransform(image, pal, GENERAL_SPRITE_COLOUR(rnd_colour));
00205
00206 if ((byte)dtss->delta_z != 0x80) {
00207 AddSortableSpriteToDraw(
00208 image, pal,
00209 ti->x + dtss->delta_x, ti->y + dtss->delta_y,
00210 dtss->size_x, dtss->size_y,
00211 dtss->size_z, ti->z + dtss->delta_z,
00212 !HasBit(image, SPRITE_MODIFIER_OPAQUE) && IsTransparencySet(TO_INDUSTRIES)
00213 );
00214 } else {
00215
00216 AddChildSpriteScreen(image, pal, (byte)dtss->delta_x, (byte)dtss->delta_y, !HasBit(image, SPRITE_MODIFIER_OPAQUE) && IsTransparencySet(TO_INDUSTRIES));
00217 }
00218 }
00219 }
00220
00221 uint16 GetIndustryTileCallback(CallbackID callback, uint32 param1, uint32 param2, IndustryGfx gfx_id, Industry *industry, TileIndex tile)
00222 {
00223 ResolverObject object;
00224 const SpriteGroup *group;
00225
00226 NewIndustryTileResolver(&object, gfx_id, tile, industry);
00227 object.callback = callback;
00228 object.callback_param1 = param1;
00229 object.callback_param2 = param2;
00230
00231 group = SpriteGroup::Resolve(GetIndustryTileSpec(gfx_id)->grf_prop.spritegroup, &object);
00232 if (group == NULL || group->type != SGT_CALLBACK) return CALLBACK_FAILED;
00233
00234 return group->GetCallbackResult();
00235 }
00236
00237 bool DrawNewIndustryTile(TileInfo *ti, Industry *i, IndustryGfx gfx, const IndustryTileSpec *inds)
00238 {
00239 const SpriteGroup *group;
00240 ResolverObject object;
00241
00242 if (ti->tileh != SLOPE_FLAT) {
00243 bool draw_old_one = true;
00244 if (HasBit(inds->callback_mask, CBM_INDT_DRAW_FOUNDATIONS)) {
00245
00246 uint32 callback_res = GetIndustryTileCallback(CBID_INDUSTRY_DRAW_FOUNDATIONS, 0, 0, gfx, i, ti->tile);
00247 draw_old_one = (callback_res != 0);
00248 }
00249
00250 if (draw_old_one) DrawFoundation(ti, FOUNDATION_LEVELED);
00251 }
00252
00253 NewIndustryTileResolver(&object, gfx, ti->tile, i);
00254
00255 group = SpriteGroup::Resolve(inds->grf_prop.spritegroup, &object);
00256 const TileLayoutSpriteGroup *tlgroup = (const TileLayoutSpriteGroup *)group;
00257 if (group == NULL || group->type != SGT_TILELAYOUT || tlgroup->num_building_stages == 0) {
00258 return false;
00259 } else {
00260
00261 byte stage = GetIndustryConstructionStage(ti->tile);
00262 stage = Clamp(stage - 4 + tlgroup->num_building_stages, 0, tlgroup->num_building_stages - 1);
00263 IndustryDrawTileLayout(ti, tlgroup, i->random_colour, stage, gfx);
00264 return true;
00265 }
00266 }
00267
00268 extern bool IsSlopeRefused(Slope current, Slope refused);
00269
00270 bool PerformIndustryTileSlopeCheck(TileIndex ind_base_tile, TileIndex ind_tile, const IndustryTileSpec *its, IndustryType type, IndustryGfx gfx, uint itspec_index)
00271 {
00272 Industry ind;
00273 ind.index = INVALID_INDUSTRY;
00274 ind.xy = ind_base_tile;
00275 ind.width = 0;
00276 ind.type = type;
00277
00278 uint16 callback_res = GetIndustryTileCallback(CBID_INDTILE_SHAPE_CHECK, 0, itspec_index, gfx, &ind, ind_tile);
00279 if (callback_res == CALLBACK_FAILED) {
00280 return !IsSlopeRefused(GetTileSlope(ind_tile, NULL), its->slopes_refused);
00281 }
00282 if (its->grf_prop.grffile->grf_version < 7) {
00283 return callback_res != 0;
00284 }
00285 if (callback_res == 0x400) return true;
00286
00287
00288 SwitchToErrorRefStack();
00289 PrepareTextRefStackUsage(4);
00290 SwitchToNormalRefStack();
00291
00292 switch (callback_res) {
00293 case 0x401: _error_message = STR_ERROR_SITE_UNSUITABLE; return false;
00294 case 0x402: _error_message = STR_ERROR_CAN_ONLY_BE_BUILT_IN_RAINFOREST; return false;
00295 case 0x403: _error_message = STR_ERROR_CAN_ONLY_BE_BUILT_IN_DESERT; return false;
00296 default: _error_message = GetGRFStringID(its->grf_prop.grffile->grfid, 0xD000 + callback_res); return false;
00297 }
00298 }
00299
00300 void AnimateNewIndustryTile(TileIndex tile)
00301 {
00302 Industry *ind = Industry::GetByTile(tile);
00303 IndustryGfx gfx = GetIndustryGfx(tile);
00304 const IndustryTileSpec *itspec = GetIndustryTileSpec(gfx);
00305 byte animation_speed = itspec->animation_speed;
00306
00307 if (HasBit(itspec->callback_mask, CBM_INDT_ANIM_SPEED)) {
00308 uint16 callback_res = GetIndustryTileCallback(CBID_INDTILE_ANIMATION_SPEED, 0, 0, gfx, ind, tile);
00309 if (callback_res != CALLBACK_FAILED) animation_speed = Clamp(callback_res & 0xFF, 0, 16);
00310 }
00311
00312
00313
00314
00315
00316 if ((_tick_counter % (1 << animation_speed)) != 0) return;
00317
00318 bool frame_set_by_callback = false;
00319 byte frame = GetIndustryAnimationState(tile);
00320 uint16 num_frames = GB(itspec->animation_info, 0, 8);
00321
00322 if (HasBit(itspec->callback_mask, CBM_INDT_ANIM_NEXT_FRAME)) {
00323 uint16 callback_res = GetIndustryTileCallback(CBID_INDTILE_ANIM_NEXT_FRAME, HasBit(itspec->animation_special_flags, 0) ? Random() : 0, 0, gfx, ind, tile);
00324
00325 if (callback_res != CALLBACK_FAILED) {
00326 frame_set_by_callback = true;
00327
00328 switch (callback_res & 0xFF) {
00329 case 0xFF:
00330 DeleteAnimatedTile(tile);
00331 break;
00332 case 0xFE:
00333
00334 frame_set_by_callback = false;
00335 break;
00336 default:
00337 frame = callback_res & 0xFF;
00338 break;
00339 }
00340
00341
00342
00343 if (GB(callback_res, 8, 7) != 0) PlayTileSound(itspec->grf_prop.grffile, GB(callback_res, 8, 7), tile);
00344 }
00345 }
00346
00347 if (!frame_set_by_callback) {
00348 if (frame < num_frames) {
00349 frame++;
00350 } else if (frame == num_frames && GB(itspec->animation_info, 8, 8) == 1) {
00351
00352 frame = 0;
00353 } else {
00354
00355 DeleteAnimatedTile(tile);
00356 }
00357 }
00358
00359 SetIndustryAnimationState(tile, frame);
00360 MarkTileDirtyByTile(tile);
00361 }
00362
00363 static void ChangeIndustryTileAnimationFrame(const IndustryTileSpec *itspec, TileIndex tile, IndustryAnimationTrigger iat, uint32 random_bits, IndustryGfx gfx, Industry *ind)
00364 {
00365 uint16 callback_res = GetIndustryTileCallback(CBID_INDTILE_ANIM_START_STOP, random_bits, iat, gfx, ind, tile);
00366 if (callback_res == CALLBACK_FAILED) return;
00367
00368 switch (callback_res & 0xFF) {
00369 case 0xFD: break;
00370 case 0xFE: AddAnimatedTile(tile); break;
00371 case 0xFF: DeleteAnimatedTile(tile); break;
00372 default:
00373 SetIndustryAnimationState(tile, callback_res & 0xFF);
00374 AddAnimatedTile(tile);
00375 break;
00376 }
00377
00378
00379
00380 if (GB(callback_res, 8, 7) != 0) PlayTileSound(itspec->grf_prop.grffile, GB(callback_res, 8, 7), tile);
00381 }
00382
00383 bool StartStopIndustryTileAnimation(TileIndex tile, IndustryAnimationTrigger iat, uint32 random)
00384 {
00385 IndustryGfx gfx = GetIndustryGfx(tile);
00386 const IndustryTileSpec *itspec = GetIndustryTileSpec(gfx);
00387
00388 if (!HasBit(itspec->animation_triggers, iat)) return false;
00389
00390 Industry *ind = Industry::GetByTile(tile);
00391 ChangeIndustryTileAnimationFrame(itspec, tile, iat, random, gfx, ind);
00392 return true;
00393 }
00394
00395 bool StartStopIndustryTileAnimation(const Industry *ind, IndustryAnimationTrigger iat)
00396 {
00397 bool ret = true;
00398 uint32 random = Random();
00399 TILE_LOOP(tile, ind->width, ind->height, ind->xy) {
00400 if (IsTileType(tile, MP_INDUSTRY) && GetIndustryIndex(tile) == ind->index) {
00401 if (StartStopIndustryTileAnimation(tile, iat, random)) {
00402 SB(random, 0, 16, Random());
00403 } else {
00404 ret = false;
00405 }
00406 }
00407 }
00408
00409 return ret;
00410 }
00411
00412 static void DoTriggerIndustryTile(TileIndex tile, IndustryTileTrigger trigger, Industry *ind)
00413 {
00414 ResolverObject object;
00415
00416 IndustryGfx gfx = GetIndustryGfx(tile);
00417 const IndustryTileSpec *itspec = GetIndustryTileSpec(gfx);
00418
00419 if (itspec->grf_prop.spritegroup == NULL) return;
00420
00421 NewIndustryTileResolver(&object, gfx, tile, ind);
00422
00423 object.callback = CBID_RANDOM_TRIGGER;
00424 object.trigger = trigger;
00425
00426 const SpriteGroup *group = SpriteGroup::Resolve(itspec->grf_prop.spritegroup, &object);
00427 if (group == NULL) return;
00428
00429 byte new_random_bits = Random();
00430 byte random_bits = GetIndustryRandomBits(tile);
00431 random_bits &= ~object.reseed;
00432 random_bits |= new_random_bits & object.reseed;
00433 SetIndustryRandomBits(tile, random_bits);
00434 MarkTileDirtyByTile(tile);
00435 }
00436
00437 void TriggerIndustryTile(TileIndex tile, IndustryTileTrigger trigger)
00438 {
00439 DoTriggerIndustryTile(tile, trigger, Industry::GetByTile(tile));
00440 }
00441
00442 void TriggerIndustry(Industry *ind, IndustryTileTrigger trigger)
00443 {
00444 TILE_LOOP(tile, ind->width, ind->height, ind->xy) {
00445 if (IsTileType(tile, MP_INDUSTRY) && GetIndustryIndex(tile) == ind->index) {
00446 DoTriggerIndustryTile(tile, trigger, ind);
00447 }
00448 }
00449 }