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