1 #include "connectable.h"
2
3 #include "building/building.h"
4 #include "building/construction.h"
5 #include "building/image.h"
6 #include "building/rotation.h"
7 #include "city/view.h"
8 #include "map/building.h"
9 #include "map/building_tiles.h"
10 #include "map/grid.h"
11 #include "map/image.h"
12 #include "map/property.h"
13 #include "map/random.h"
14 #include "map/terrain.h"
15
16 #define MAX_TILES 8
17
18 typedef struct {
19 const unsigned char tiles[MAX_TILES];
20 const unsigned char offset_for_orientation[4];
21 const int rotation;
22 const unsigned char terrain_tiles[MAX_TILES];
23 const int use_terrain;
24 const int max_random;
25 } building_image_context;
26
27 static const building_type connectable_buildings[] = {
28 BUILDING_HEDGE_DARK,
29 BUILDING_HEDGE_LIGHT,
30 BUILDING_COLONNADE,
31 BUILDING_GARDEN_PATH,
32 BUILDING_DATE_PATH,
33 BUILDING_ELM_PATH,
34 BUILDING_FIG_PATH,
35 BUILDING_FIR_PATH,
36 BUILDING_OAK_PATH,
37 BUILDING_PALM_PATH,
38 BUILDING_PINE_PATH,
39 BUILDING_PLUM_PATH,
40 BUILDING_GARDEN_WALL,
41 BUILDING_ROOFED_GARDEN_WALL,
42 BUILDING_GARDEN_WALL_GATE,
43 BUILDING_PALISADE,
44 };
45
46 static const int MAX_CONNECTABLE_BUILDINGS = sizeof(connectable_buildings) / sizeof(building_type);
47
48 // 0 = no match
49 // 1 = match
50 // 2 = don't care
51
52 // For rotation
53 // -1 any, otherwise shown value
54
55 static const building_image_context building_images_hedges[18] = {
56 { { 1, 2, 1, 2, 0, 2, 0, 2 }, { 4, 5, 2, 3 }, -1 },
57 { { 0, 2, 1, 2, 1, 2, 0, 2 }, { 3, 4, 5, 2 }, -1 },
58 { { 0, 2, 0, 2, 1, 2, 1, 2 }, { 2, 3, 4, 5 }, -1 },
59 { { 1, 2, 0, 2, 0, 2, 1, 2 }, { 5, 2, 3, 4 }, -1 },
60 { { 1, 2, 0, 2, 1, 2, 0, 2 }, { 1, 0, 1, 0 }, -1 },
61 { { 0, 2, 1, 2, 0, 2, 1, 2 }, { 0, 1, 0, 1 }, -1 },
62 { { 1, 2, 0, 2, 0, 2, 0, 2 }, { 1, 0, 1, 0 }, -1 },
63 { { 0, 2, 1, 2, 0, 2, 0, 2 }, { 0, 1, 0, 1 }, -1 },
64 { { 0, 2, 0, 2, 1, 2, 0, 2 }, { 1, 0, 1, 0 }, -1 },
65 { { 0, 2, 0, 2, 0, 2, 1, 2 }, { 0, 1, 0, 1 }, -1 },
66 { { 1, 2, 1, 2, 1, 2, 0, 2 }, { 9, 7, 6, 8 }, -1 },
67 { { 0, 2, 1, 2, 1, 2, 1, 2 }, { 8, 9, 7, 6 }, -1 },
68 { { 1, 2, 0, 2, 1, 2, 1, 2 }, { 6, 8, 9, 7 }, -1 },
69 { { 1, 2, 1, 2, 0, 2, 1, 2 }, { 7, 6, 8, 9 }, -1 },
70 { { 1, 2, 1, 2, 1, 2, 1, 2 }, { 10, 10, 10, 10 }, -1 },
71 { { 2, 2, 2, 2, 2, 2, 2, 2 }, { 1, 0, 1, 0 }, 0 },
72 { { 2, 2, 2, 2, 2, 2, 2, 2 }, { 0, 1, 0, 1 }, 1 },
73 { { 2, 2, 2, 2, 2, 2, 2, 2 }, { 10, 10, 10, 10 }, -1 },
74 };
75
76 static const building_image_context building_images_path_intersection[9] = {
77 { { 1, 2, 1, 2, 0, 2, 0, 2 }, { 2, 3, 0, 1 }, -1 },
78 { { 0, 2, 1, 2, 1, 2, 0, 2 }, { 1, 2, 3, 0 }, -1 },
79 { { 0, 2, 0, 2, 1, 2, 1, 2 }, { 0, 1, 2, 3 }, -1 },
80 { { 1, 2, 0, 2, 0, 2, 1, 2 }, { 3, 0, 1, 2 }, -1 },
81 { { 1, 2, 1, 2, 1, 2, 0, 2 }, { 5, 6, 7, 4 }, -1 },
82 { { 0, 2, 1, 2, 1, 2, 1, 2 }, { 4, 5, 6, 7 }, -1 },
83 { { 1, 2, 0, 2, 1, 2, 1, 2 }, { 7, 4, 5, 6 }, -1 },
84 { { 1, 2, 1, 2, 0, 2, 1, 2 }, { 6, 7, 4, 5 }, -1 },
85 { { 1, 2, 1, 2, 1, 2, 1, 2 }, { 8, 8, 8, 8 }, -1 },
86 };
87
88 static const building_image_context building_images_tree_path[8] = {
89 { { 1, 2, 0, 2, 1, 2, 0, 2 }, { 0, 67, 0, 67 }, -1 },
90 { { 0, 2, 1, 2, 0, 2, 1, 2 }, { 67, 0, 67, 0 }, -1 },
91 { { 1, 2, 0, 2, 0, 2, 0, 2 }, { 0, 67, 0, 67 }, -1 },
92 { { 0, 2, 1, 2, 0, 2, 0, 2 }, { 67, 0, 67, 0 }, -1 },
93 { { 0, 2, 0, 2, 1, 2, 0, 2 }, { 0, 67, 0, 67 }, -1 },
94 { { 0, 2, 0, 2, 0, 2, 1, 2 }, { 67, 0, 67, 0 }, -1 },
95 { { 2, 2, 2, 2, 2, 2, 2, 2 }, { 0, 67, 0, 67 }, 0 },
96 { { 2, 2, 2, 2, 2, 2, 2, 2 }, { 67, 0, 67, 0 }, -1 },
97 };
98
99 static const building_image_context building_images_treeless_path[8] = {
100 { { 1, 2, 0, 2, 1, 2, 0, 2 }, { 54, 0, 54, 0 }, -1 },
101 { { 0, 2, 1, 2, 0, 2, 1, 2 }, { 0, 54, 0, 54 }, -1 },
102 { { 1, 2, 0, 2, 0, 2, 0, 2 }, { 54, 0, 54, 0 }, -1 },
103 { { 0, 2, 1, 2, 0, 2, 0, 2 }, { 0, 54, 0, 54 }, -1 },
104 { { 0, 2, 0, 2, 1, 2, 0, 2 }, { 54, 0, 54, 0 }, -1 },
105 { { 0, 2, 0, 2, 0, 2, 1, 2 }, { 0, 54, 0, 54 }, -1 },
106 { { 2, 2, 2, 2, 2, 2, 2, 2 }, { 54, 0, 54, 0 }, 0 },
107 { { 2, 2, 2, 2, 2, 2, 2, 2 }, { 0, 54, 0, 54 }, -1 },
108 };
109
110 static const building_image_context building_images_garden_gate[14] = {
111 { { 1, 2, 0, 2, 1, 2, 0, 2 }, { 2, 0, 2, 0 }, -1,},
112 { { 0, 2, 1, 2, 0, 2, 1, 2 }, { 0, 2, 0, 2 }, -1,},
113 { { 1, 2, 0, 2, 0, 2, 0, 2 }, { 2, 0, 2, 0 }, -1 },
114 { { 0, 2, 1, 2, 0, 2, 0, 2 }, { 0, 2, 0, 2 }, -1 },
115 { { 0, 2, 0, 2, 1, 2, 0, 2 }, { 2, 0, 2, 0 }, -1 },
116 { { 0, 2, 0, 2, 0, 2, 1, 2 }, { 0, 2, 0, 2 }, -1 },
117 { { 2, 2, 2, 2, 2, 2, 2, 2 }, { 0, 2, 0, 2 }, -1, { 1, 2, 0, 2, 1, 2, 0, 2 }, 1 },
118 { { 2, 2, 2, 2, 2, 2, 2, 2 }, { 2, 0, 2, 0 }, -1, { 0, 2, 1, 2, 0, 2, 1, 2 }, 1 },
119 { { 2, 2, 2, 2, 2, 2, 2, 2 }, { 0, 2, 0, 2 }, -1, { 1, 2, 0, 2, 0, 2, 0, 2 }, 1 },
120 { { 2, 2, 2, 2, 2, 2, 2, 2 }, { 2, 0, 2, 0 }, -1, { 0, 2, 1, 2, 0, 2, 0, 2 }, 1 },
121 { { 2, 2, 2, 2, 2, 2, 2, 2 }, { 0, 2, 0, 2 }, -1, { 0, 2, 0, 2, 1, 2, 0, 2 }, 1 },
122 { { 2, 2, 2, 2, 2, 2, 2, 2 }, { 2, 0, 2, 0 }, -1, { 0, 2, 0, 2, 0, 2, 1, 2 }, 1 },
123 { { 2, 2, 2, 2, 2, 2, 2, 2 }, { 2, 0, 2, 0 }, 0 },
124 { { 2, 2, 2, 2, 2, 2, 2, 2 }, { 0, 2, 0, 2 }, -1 },
125 };
126
127 static const building_image_context building_images_palisades[18] = {
128 { { 1, 2, 1, 2, 0, 2, 0, 2 }, { 15, 14, 13, 12 }, -1, { 0 }, 0, 0 },
129 { { 0, 2, 1, 2, 1, 2, 0, 2 }, { 12, 15, 14, 13 }, -1, { 0 }, 0, 0 },
130 { { 0, 2, 0, 2, 1, 2, 1, 2 }, { 13, 12, 15, 14 }, -1, { 0 }, 0, 0 },
131 { { 1, 2, 0, 2, 0, 2, 1, 2 }, { 14, 13, 12, 15 }, -1, { 0 }, 0, 0 },
132 { { 1, 2, 0, 2, 1, 2, 0, 2 }, { 6, 0, 6, 0 }, -1, { 0 }, 0, 6 },
133 { { 0, 2, 1, 2, 0, 2, 1, 2 }, { 0, 6, 0, 6 }, -1, { 0 }, 0, 6 },
134 { { 1, 2, 0, 2, 0, 2, 0, 2 }, { 6, 0, 6, 0 }, -1, { 0 }, 0, 6 },
135 { { 0, 2, 1, 2, 0, 2, 0, 2 }, { 0, 6, 0, 6 }, -1, { 0 }, 0, 6 },
136 { { 0, 2, 0, 2, 1, 2, 0, 2 }, { 6, 0, 6, 0 }, -1, { 0 }, 0, 6 },
137 { { 0, 2, 0, 2, 0, 2, 1, 2 }, { 0, 6, 0, 6 }, -1, { 0 }, 0, 6 },
138 { { 1, 2, 1, 2, 1, 2, 0, 2 }, { 19, 17, 16, 18 }, -1, { 0 }, 0, 0 },
139 { { 0, 2, 1, 2, 1, 2, 1, 2 }, { 18, 19, 17, 16 }, -1, { 0 }, 0, 0 },
140 { { 1, 2, 0, 2, 1, 2, 1, 2 }, { 16, 18, 19, 17 }, -1, { 0 }, 0, 0 },
141 { { 1, 2, 1, 2, 0, 2, 1, 2 }, { 17, 16, 18, 19 }, -1, { 0 }, 0, 0 },
142 { { 1, 2, 1, 2, 1, 2, 1, 2 }, { 20, 20, 20, 20 }, -1, { 0 }, 0, 0 },
143 { { 2, 2, 2, 2, 2, 2, 2, 2 }, { 6, 0, 6, 0 }, 0, { 0 }, 0, 6 },
144 { { 2, 2, 2, 2, 2, 2, 2, 2 }, { 0, 6, 0, 6 }, 1, { 0 }, 0, 6 },
145 { { 2, 2, 2, 2, 2, 2, 2, 2 }, { 20, 20, 20, 20 }, -1, { 0 }, 0, 0 },
146 };
147
148 static struct {
149 const building_image_context *context;
150 int size;
151 } context_pointers[] = {
152 { building_images_hedges, 18 },
153 { building_images_hedges, 18 },
154 { building_images_tree_path, 8 },
155 { building_images_path_intersection, 9 },
156 { building_images_treeless_path, 8 },
157 { building_images_hedges, 18 },
158 { building_images_garden_gate, 14},
159 { building_images_palisades, 18 },
160 };
161
building_connectable_gate_type(building_type type)162 int building_connectable_gate_type(building_type type)
163 {
164 switch (type) {
165 case BUILDING_GARDEN_WALL:
166 case BUILDING_ROOFED_GARDEN_WALL:
167 return BUILDING_GARDEN_WALL_GATE;
168 default:
169 return 0;
170 }
171 }
172
context_matches_tiles(const building_image_context * context,const int tiles[MAX_TILES],int rotation,int terrain_tiles[MAX_TILES])173 static int context_matches_tiles(const building_image_context *context,
174 const int tiles[MAX_TILES], int rotation, int terrain_tiles[MAX_TILES])
175 {
176 for (int i = 0; i < MAX_TILES; i++) {
177 if (context->use_terrain) {
178 if (context->terrain_tiles[i] != 2 && terrain_tiles[i] != context->terrain_tiles[i]) {
179 return 0;
180 }
181 }
182 if (context->tiles[i] != 2 && tiles[i] != context->tiles[i]) {
183 return 0;
184 }
185 }
186
187 return context->rotation == -1 || context->rotation == rotation;
188 }
189
get_image_offset(int group,int tiles[MAX_TILES],int rotation,int terrain_tiles[MAX_TILES],int grid_offset)190 static int get_image_offset(int group, int tiles[MAX_TILES], int rotation, int terrain_tiles[MAX_TILES], int grid_offset)
191 {
192 const building_image_context *context = context_pointers[group].context;
193 int size = context_pointers[group].size;
194 for (int i = 0; i < size; i++) {
195 if (context_matches_tiles(&context[i], tiles, rotation, terrain_tiles)) {
196 int offset = context[i].offset_for_orientation[city_view_orientation() / 2];
197 if (context[i].max_random) {
198 offset += map_random_get(grid_offset) % context[i].max_random;
199 }
200 return offset;
201 }
202 }
203 return -1;
204 }
205
building_connectable_get_hedge_offset(int grid_offset)206 int building_connectable_get_hedge_offset(int grid_offset)
207 {
208 int tiles[MAX_TILES] = { 0 };
209 for (int i = 0; i < MAX_TILES; i += 2) {
210 int offset = grid_offset + map_grid_direction_delta(i);
211 if (!map_terrain_is(offset, TERRAIN_BUILDING) && !map_property_is_constructing(offset))
212 {
213 continue;
214 }
215 building *b = building_get(map_building_at(offset));
216 if (b->type == BUILDING_HEDGE_DARK || b->type == BUILDING_HEDGE_LIGHT ||
217 (map_property_is_constructing(offset) &&
218 (building_construction_type() == BUILDING_HEDGE_DARK || building_construction_type() == BUILDING_HEDGE_LIGHT))) {
219 tiles[i] = 1;
220 }
221 }
222 int building_id = map_building_at(grid_offset);
223 int rotation;
224 if (building_id) {
225 rotation = building_get(building_id)->subtype.orientation;
226 } else {
227 rotation = building_rotation_get_rotation_with_limit(BUILDING_CONNECTABLE_ROTATION_LIMIT_HEDGES);
228 }
229 return get_image_offset(CONTEXT_HEDGES, tiles, rotation, 0, grid_offset);
230 }
231
building_connectable_get_colonnade_offset(int grid_offset)232 int building_connectable_get_colonnade_offset(int grid_offset)
233 {
234 int tiles[MAX_TILES] = { 0 };
235 for (int i = 0; i < MAX_TILES; i += 2) {
236 int offset = grid_offset + map_grid_direction_delta(i);
237 if (!map_terrain_is(offset, TERRAIN_BUILDING) && !map_property_is_constructing(offset)) {
238 continue;
239 }
240 building *b = building_get(map_building_at(offset));
241 if (b->type == BUILDING_COLONNADE ||
242 (map_property_is_constructing(offset) && building_construction_type() == BUILDING_COLONNADE)) {
243 tiles[i] = 1;
244 }
245 }
246 int building_id = map_building_at(grid_offset);
247 int rotation;
248 if (building_id) {
249 rotation = building_get(building_id)->subtype.orientation;
250 } else {
251 rotation = building_rotation_get_rotation_with_limit(BUILDING_CONNECTABLE_ROTATION_LIMIT_HEDGES);
252 }
253 return get_image_offset(CONTEXT_COLONNADE, tiles, rotation, 0, grid_offset);
254 }
255
is_garden_path(building_type type)256 static int is_garden_path(building_type type)
257 {
258 return type == BUILDING_DATE_PATH || type == BUILDING_ELM_PATH || type == BUILDING_FIG_PATH ||
259 type == BUILDING_FIR_PATH || type == BUILDING_OAK_PATH || type == BUILDING_PALM_PATH ||
260 type == BUILDING_PINE_PATH || type == BUILDING_PLUM_PATH || type == BUILDING_GARDEN_PATH;
261 }
262
is_garden_wall_or_gate(building_type type)263 static int is_garden_wall_or_gate(building_type type)
264 {
265 return type == BUILDING_GARDEN_WALL || type == BUILDING_ROOFED_GARDEN_WALL || type == BUILDING_GARDEN_WALL_GATE;
266 }
267
is_garden_wall(building_type type)268 static int is_garden_wall(building_type type)
269 {
270 return type == BUILDING_GARDEN_WALL || type == BUILDING_ROOFED_GARDEN_WALL;
271 }
272
building_connectable_get_garden_wall_offset(int grid_offset)273 int building_connectable_get_garden_wall_offset(int grid_offset)
274 {
275 int tiles[MAX_TILES] = { 0 };
276 for (int i = 0; i < MAX_TILES; i += 2) {
277 int offset = grid_offset + map_grid_direction_delta(i);
278 if (!map_terrain_is(offset, TERRAIN_BUILDING) && !map_property_is_constructing(offset)) {
279 continue;
280 }
281 building *b = building_get(map_building_at(offset));
282 if (is_garden_wall_or_gate(b->type) || (map_property_is_constructing(offset) && is_garden_wall_or_gate(building_construction_type()))) {
283 tiles[i] = 1;
284 }
285 }
286 int building_id = map_building_at(grid_offset);
287 int rotation;
288 if (building_id) {
289 rotation = building_get(building_id)->subtype.orientation;
290 } else {
291 rotation = building_rotation_get_rotation_with_limit(BUILDING_CONNECTABLE_ROTATION_LIMIT_HEDGES);
292 }
293 return get_image_offset(CONTEXT_GARDEN_WALLS, tiles, rotation, 0, grid_offset);
294 }
295
building_connectable_get_garden_path_offset(int grid_offset,int context)296 int building_connectable_get_garden_path_offset(int grid_offset, int context)
297 {
298 int tiles[MAX_TILES] = { 0 };
299 for (int i = 0; i < MAX_TILES; i += 2) {
300 int offset = grid_offset + map_grid_direction_delta(i);
301 if (!map_terrain_is(offset, TERRAIN_BUILDING) && !map_property_is_constructing(offset)) {
302 continue;
303 }
304 building *b = building_get(map_building_at(offset));
305 if (is_garden_path(b->type) || (map_property_is_constructing(offset) && is_garden_path(building_construction_type()))) {
306 tiles[i] = 1;
307 }
308 }
309 int building_id = map_building_at(grid_offset);
310 int rotation;
311 if (building_id) {
312 rotation = building_get(building_id)->subtype.orientation;
313 } else {
314 rotation = building_rotation_get_rotation_with_limit(BUILDING_CONNECTABLE_ROTATION_LIMIT_PATHS);
315 }
316 return get_image_offset(context, tiles, rotation, 0, grid_offset);
317 }
318
building_connectable_get_garden_gate_offset(int grid_offset)319 int building_connectable_get_garden_gate_offset(int grid_offset)
320 {
321 int tiles[MAX_TILES] = { 0 };
322 int terrain_tiles[MAX_TILES] = { 0 };
323 for (int i = 0; i < MAX_TILES; i += 2) {
324 int offset = grid_offset + map_grid_direction_delta(i);
325
326 if (map_terrain_is(offset, TERRAIN_ROAD)) {
327 terrain_tiles[i] = 1;
328 }
329
330 if (!map_terrain_is(offset, TERRAIN_BUILDING) && !map_property_is_constructing(offset)) {
331 continue;
332 }
333 building *b = building_get(map_building_at(offset));
334 if (is_garden_wall(b->type) ||
335 (map_property_is_constructing(offset) && !map_terrain_is(offset, TERRAIN_ROAD) && is_garden_wall(building_construction_type()))) {
336 tiles[i] = 1;
337 }
338 }
339 int building_id = map_building_at(grid_offset);
340 int rotation;
341 if (building_id) {
342 rotation = building_get(building_id)->subtype.orientation;
343 } else {
344 rotation = building_rotation_get_rotation_with_limit(BUILDING_CONNECTABLE_ROTATION_LIMIT_PATHS);
345 }
346 return get_image_offset(CONTEXT_GARDEN_GATE, tiles, rotation, terrain_tiles, grid_offset);
347 }
348
building_connectable_get_palisade_offset(int grid_offset)349 int building_connectable_get_palisade_offset(int grid_offset)
350 {
351 int tiles[MAX_TILES] = { 0 };
352 for (int i = 0; i < MAX_TILES; i += 2) {
353 int offset = grid_offset + map_grid_direction_delta(i);
354 if (!map_terrain_is(offset, TERRAIN_BUILDING) && !map_property_is_constructing(offset)) {
355 continue;
356 }
357 building *b = building_get(map_building_at(offset));
358 if (b->type == BUILDING_PALISADE ||
359 (map_property_is_constructing(offset) && building_construction_type() == BUILDING_PALISADE)) {
360 tiles[i] = 1;
361 }
362 }
363 int building_id = map_building_at(grid_offset);
364 int rotation;
365 if (building_id) {
366 rotation = building_get(building_id)->subtype.orientation;
367 } else {
368 rotation = building_rotation_get_rotation_with_limit(BUILDING_CONNECTABLE_ROTATION_LIMIT_HEDGES);
369 }
370 return get_image_offset(CONTEXT_PALISADES, tiles, rotation, 0, grid_offset);
371 }
372
building_is_connectable(building_type type)373 int building_is_connectable(building_type type)
374 {
375 for (int i = 0; i < MAX_CONNECTABLE_BUILDINGS; i++) {
376 if (type == connectable_buildings[i]) {
377 return 1;
378 }
379 }
380 return 0;
381 }
382
building_connectable_update_connections_for_type(building_type type)383 void building_connectable_update_connections_for_type(building_type type)
384 {
385 for (building *b = building_first_of_type(type); b; b = b->next_of_type) {
386 map_image_set(b->grid_offset, building_image_get(b));
387 }
388 }
389
building_connectable_update_connections(void)390 void building_connectable_update_connections(void)
391 {
392 for (int i = 0; i < MAX_CONNECTABLE_BUILDINGS; i++) {
393 building_connectable_update_connections_for_type(connectable_buildings[i]);
394 }
395 }
396