1 /*
2  * This file is part of OpenTTD.
3  * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2.
4  * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
5  * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see <http://www.gnu.org/licenses/>.
6  */
7 
8 /** @file newgrf_canal.cpp Implementation of NewGRF canals. */
9 
10 #include "stdafx.h"
11 #include "debug.h"
12 #include "newgrf_spritegroup.h"
13 #include "newgrf_canal.h"
14 #include "water.h"
15 #include "water_map.h"
16 #include "spritecache.h"
17 
18 #include "safeguards.h"
19 
20 /** Table of canal 'feature' sprite groups */
21 WaterFeature _water_feature[CF_END];
22 
23 /** Scope resolver of a canal tile. */
24 struct CanalScopeResolver : public ScopeResolver {
25 	TileIndex tile; ///< Tile containing the canal.
26 
CanalScopeResolverCanalScopeResolver27 	CanalScopeResolver(ResolverObject &ro, TileIndex tile)
28 		: ScopeResolver(ro), tile(tile)
29 	{
30 	}
31 
32 	uint32 GetRandomBits() const override;
33 	uint32 GetVariable(byte variable, uint32 parameter, bool *available) const override;
34 };
35 
36 /** Resolver object for canals. */
37 struct CanalResolverObject : public ResolverObject {
38 	CanalScopeResolver canal_scope;
39 	CanalFeature feature;
40 
41 	CanalResolverObject(CanalFeature feature, TileIndex tile,
42 			CallbackID callback = CBID_NO_CALLBACK, uint32 callback_param1 = 0, uint32 callback_param2 = 0);
43 
GetScopeCanalResolverObject44 	ScopeResolver *GetScope(VarSpriteGroupScope scope = VSG_SCOPE_SELF, byte relative = 0) override
45 	{
46 		switch (scope) {
47 			case VSG_SCOPE_SELF: return &this->canal_scope;
48 			default: return ResolverObject::GetScope(scope, relative);
49 		}
50 	}
51 
52 	GrfSpecFeature GetFeature() const override;
53 	uint32 GetDebugID() const override;
54 };
55 
GetRandomBits() const56 /* virtual */ uint32 CanalScopeResolver::GetRandomBits() const
57 {
58 	/* Return random bits only for water tiles, not station tiles */
59 	return IsTileType(this->tile, MP_WATER) ? GetWaterTileRandomBits(this->tile) : 0;
60 }
61 
GetVariable(byte variable,uint32 parameter,bool * available) const62 /* virtual */ uint32 CanalScopeResolver::GetVariable(byte variable, uint32 parameter, bool *available) const
63 {
64 	switch (variable) {
65 		/* Height of tile */
66 		case 0x80: {
67 			int z = GetTileZ(this->tile);
68 			/* Return consistent height within locks */
69 			if (IsTileType(this->tile, MP_WATER) && IsLock(this->tile) && GetLockPart(this->tile) == LOCK_PART_UPPER) z--;
70 			return z;
71 		}
72 
73 		/* Terrain type */
74 		case 0x81: return GetTerrainType(this->tile);
75 
76 		/* Dike map: Connectivity info for river and canal tiles
77 		 *
78 		 * Assignment of bits to directions defined in agreement with
79 		 * http://projects.tt-forums.net/projects/ttdpatch/repository/revisions/2367/entry/trunk/patches/water.asm#L879
80 		 *         7
81 		 *      3     0
82 		 *   6     *     4
83 		 *      2     1
84 		 *         5
85 		 */
86 		case 0x82: {
87 			uint32 connectivity =
88 				  (!IsWateredTile(TILE_ADDXY(tile, -1,  0), DIR_SW) << 0)  // NE
89 				+ (!IsWateredTile(TILE_ADDXY(tile,  0,  1), DIR_NW) << 1)  // SE
90 				+ (!IsWateredTile(TILE_ADDXY(tile,  1,  0), DIR_NE) << 2)  // SW
91 				+ (!IsWateredTile(TILE_ADDXY(tile,  0, -1), DIR_SE) << 3)  // NW
92 				+ (!IsWateredTile(TILE_ADDXY(tile, -1,  1), DIR_W)  << 4)  // E
93 				+ (!IsWateredTile(TILE_ADDXY(tile,  1,  1), DIR_N)  << 5)  // S
94 				+ (!IsWateredTile(TILE_ADDXY(tile,  1, -1), DIR_E)  << 6)  // W
95 				+ (!IsWateredTile(TILE_ADDXY(tile, -1, -1), DIR_S)  << 7); // N
96 			return connectivity;
97 		}
98 
99 		/* Random data for river or canal tiles, otherwise zero */
100 		case 0x83: return IsTileType(this->tile, MP_WATER) ? GetWaterTileRandomBits(this->tile) : 0;
101 	}
102 
103 	Debug(grf, 1, "Unhandled canal variable 0x{:02X}", variable);
104 
105 	*available = false;
106 	return UINT_MAX;
107 }
108 
GetFeature() const109 GrfSpecFeature CanalResolverObject::GetFeature() const
110 {
111 	return GSF_CANALS;
112 }
113 
GetDebugID() const114 uint32 CanalResolverObject::GetDebugID() const
115 {
116 	return this->feature;
117 }
118 
119 /**
120  * Canal resolver constructor.
121  * @param feature Which canal feature we want.
122  * @param tile Tile index of canal.
123  * @param callback Callback ID.
124  * @param callback_param1 First parameter (var 10) of the callback.
125  * @param callback_param2 Second parameter (var 18) of the callback.
126  */
CanalResolverObject(CanalFeature feature,TileIndex tile,CallbackID callback,uint32 callback_param1,uint32 callback_param2)127 CanalResolverObject::CanalResolverObject(CanalFeature feature, TileIndex tile,
128 		CallbackID callback, uint32 callback_param1, uint32 callback_param2)
129 		: ResolverObject(_water_feature[feature].grffile, callback, callback_param1, callback_param2), canal_scope(*this, tile), feature(feature)
130 {
131 	this->root_spritegroup = _water_feature[feature].group;
132 }
133 
134 /**
135  * Lookup the base sprite to use for a canal.
136  * @param feature Which canal feature we want.
137  * @param tile Tile index of canal, if appropriate.
138  * @return Base sprite returned by GRF, or \c 0 if none.
139  */
GetCanalSprite(CanalFeature feature,TileIndex tile)140 SpriteID GetCanalSprite(CanalFeature feature, TileIndex tile)
141 {
142 	CanalResolverObject object(feature, tile);
143 	const SpriteGroup *group = object.Resolve();
144 	if (group == nullptr) return 0;
145 
146 	return group->GetResult();
147 }
148 
149 /**
150  * Run a specific callback for canals.
151  * @param callback Callback ID.
152  * @param param1   Callback parameter 1.
153  * @param param2   Callback parameter 2.
154  * @param feature  For which feature to run the callback.
155  * @param tile     Tile index of canal.
156  * @return Callback result or #CALLBACK_FAILED if the callback failed.
157  */
GetCanalCallback(CallbackID callback,uint32 param1,uint32 param2,CanalFeature feature,TileIndex tile)158 static uint16 GetCanalCallback(CallbackID callback, uint32 param1, uint32 param2, CanalFeature feature, TileIndex tile)
159 {
160 	CanalResolverObject object(feature, tile, callback, param1, param2);
161 	return object.ResolveCallback();
162 }
163 
164 /**
165  * Get the new sprite offset for a water tile.
166  * @param tile       Tile index of the canal/water tile.
167  * @param feature    For which feature to get the new sprite offset.
168  * @param cur_offset Current sprite offset.
169  * @return New sprite offset.
170  */
GetCanalSpriteOffset(CanalFeature feature,TileIndex tile,uint cur_offset)171 uint GetCanalSpriteOffset(CanalFeature feature, TileIndex tile, uint cur_offset)
172 {
173 	if (HasBit(_water_feature[feature].callback_mask, CBM_CANAL_SPRITE_OFFSET)) {
174 		uint16 cb = GetCanalCallback(CBID_CANALS_SPRITE_OFFSET, cur_offset, 0, feature, tile);
175 		if (cb != CALLBACK_FAILED) return cur_offset + cb;
176 	}
177 	return cur_offset;
178 }
179