#include #include #include #include #include #include #include #include #include #include "main.h" #include "paths.h" #include "level.h" #include "items.h" #include "draw.h" #include "draw_paths.h" #define dir_t uint8_t #define path_t uint8_t #define loc_t uint8_t uint8_t paths_connections[256]; uint8_t paths_cursor_x; uint8_t paths_cursor_y; uint8_t paths_cursor_col; static bool cursor_pressed; static dir_t cursor_link_from_side; static uint8_t cursor_link_choice_next; static uint8_t coords_move(uint8_t direction, uint8_t* x, uint8_t* y) { switch (direction) { case DIR_U : --*y; break; case DIR_L : --*x; break; case DIR_R : ++*x; break; case DIR_D : ++*y; break; } return (*y)*16+(*x); } static const path_t direction_to_code[5] = {0, PATH_U, PATH_L, PATH_R, PATH_D}; static dir_t code_to_direction(path_t code) { switch (code & PATH_MASK_DIRS) { case PATH_U: return DIR_U; case PATH_L: return DIR_L; case PATH_R: return DIR_R; case PATH_D: return DIR_D; } return DIR_0; } dir_t paths_direction(path_t* p) { return code_to_direction(*p); } static path_t connection_to_code(dir_t src/* != DIR_0*/, dir_t dst/* != DIR_0*/) { static const path_t LUT[7] = { /*0110*/0x6, // PATH_FLAG_ARCZ | PATH_BITS_ARCU, // U---L /*1010*/0xA, // PATH_FLAG_ARCN | PATH_BITS_ARCU, // U---R /*0010*/0x2, // PATH_BITS_VER , // U---D /*0001*/0x1, // PATH_BITS_HOR , // L---R /*1001*/0x9, // PATH_FLAG_ARCN | PATH_BITS_ARCD, // L---D /*0000*/0x0, // 0, // R---R /*0101*/0x5, // PATH_FLAG_ARCZ | PATH_BITS_ARCD, // R---D }; if (src == dst) return 0; if (src > dst) { dir_t tmp; tmp = src; src = dst; dst = tmp; } assert(src != DIR_0); return LUT[src*2 + dst-4]; } static dir_t path_connected_to(path_t code, dir_t direction) { dir_t links[5] = {DIR_0, DIR_0, DIR_0, DIR_0, DIR_0}; if (code & PATH_FLAG_ARCN) { if (code & PATH_BITS_ARCU) { links[DIR_U] = DIR_R; links[DIR_R] = DIR_U; } if (code & PATH_BITS_ARCD) { links[DIR_L] = DIR_D; links[DIR_D] = DIR_L; } } else if (code & PATH_FLAG_ARCZ) { if (code & PATH_BITS_ARCU) { links[DIR_U] = DIR_L; links[DIR_L] = DIR_U; } if (code & PATH_BITS_ARCD) { links[DIR_R] = DIR_D; links[DIR_D] = DIR_R; } } else { if (code & PATH_BITS_VER) { links[DIR_U] = DIR_D; links[DIR_D] = DIR_U; } if (code & PATH_BITS_HOR) { links[DIR_L] = DIR_R; links[DIR_R] = DIR_L; } } return links[direction]; } static bool path_direction_add(path_t* p, dir_t direction) { return (!(*p & PATH_MASK_DIRS)) && (!path_connected_to(*p, direction)) && (*p |= direction_to_code[direction]); } #define DIRECTION_ADD(p, dir) (*(p) |= direction_to_code[dir]) #define DIRECTION_REMOVE(p, dir) (*(p) &= PATH_MASK_WAYS) #define DIRECTION_TOGGLE(p, dir) (*(p) ^= direction_to_code[dir]) static path_t* path_next_to(path_t* p, dir_t direction/* != DIR_0*/) { switch (direction) { case DIR_U: return p-16; case DIR_L: return p-1; case DIR_R: return p+1; case DIR_D: return p+16; } assert(false); // unreachable return NULL; } static bool direction_occupied(path_t code, dir_t direction) { return (code & direction_to_code[direction]) || (path_connected_to(code, direction)); } static bool paths_connect(path_t* p, dir_t src, dir_t dst/* != DIR_0*/) { path_t code = connection_to_code(src, dst); if (code) { path_t* p_next = path_next_to(p, dst); dir_t src_next = DIR_OPPOSITE(dst); if (!(direction_occupied(*p_next, src_next))) { path_t bits = *p; if ((!(bits & PATH_MASK_WAYS)) || ((bits & PATH_MASK_ARCS) == (code & PATH_MASK_ARCS))) { *p |= code; return true; } } } return false; } static bool paths_disconnect(path_t* p, dir_t src, dir_t dst) { path_t code = connection_to_code(src, dst); // if (code) { path_t bits = *p; if ((bits & PATH_MASK_ARCS) == (code & PATH_MASK_ARCS)) { if (bits & code & PATH_CROSS) { bits ^= code & PATH_CROSS; if (!(bits & PATH_CROSS)) bits &= PATH_MASK_DIRS; *p = bits; return true; } } // } return false; } dir_t paths_direction_step(path_t* p, dir_t direction) { dir_t direction_new; assert(*p & direction_to_code[direction]); DIRECTION_TOGGLE(p, direction); p = path_next_to(p, direction); if ((direction_new = path_connected_to(*p, DIR_OPPOSITE(direction))) != DIR_0) { paths_disconnect(p, DIR_OPPOSITE(direction), direction_new); DIRECTION_ADD(p, direction_new); } return direction_new; } static bool path_step(path_t** p, dir_t* direction/* != DIR_0*/) { *p = path_next_to(*p, *direction); *direction = path_connected_to(**p, DIR_OPPOSITE(*direction)); return *direction != DIR_0; } static void path_follow(path_t** p, dir_t* direction/* != DIR_0*/) { dir_t direction_last; loop: direction_last = *direction; if (path_step(p, direction)) goto loop; *direction = direction_last; } static uint16_t path_length_from(path_t* p, dir_t direction/* != DIR_0*/) { uint16_t length = 1; if (*p & direction_to_code[direction]) --length; for (; path_step(&p, &direction) ;) ++length; return length; } static void path_clear_from(path_t* p, dir_t direction/* != DIR_0*/) { bool res; if (*p & direction_to_code[direction]) { DIRECTION_REMOVE(p, direction); if (!path_step(&p, &direction)) return; } loop: res = paths_disconnect(p, path_connected_to(*p, direction), direction); assert(res); if (path_step(&p, &direction)) goto loop; } static void path_direction(path_t* p, dir_t* direction) { dir_t direction_current = *direction; dir_t direction_alt = path_connected_to(*p, direction_current); assert(direction_current != DIR_0); if (!direction_alt) { *direction = DIR_0; return; } path_follow(&p, &direction_current); if (*p & direction_to_code[DIR_OPPOSITE(direction_current)]) *direction = direction_alt; } static void path_beginning(path_t** p, dir_t direction) { dir_t direction_forward = direction; dir_t direction_backward; path_direction(*p, &direction_forward); direction_backward = path_connected_to(**p, direction_forward); path_follow(p, &direction_backward); } static void path_directions(path_t* p, dir_t* direction_forward_1, dir_t* direction_forward_2) { dir_t direction_tmp; if ((*p & PATH_CROSS) == PATH_CROSS) { path_t* p_beginning_1 = p; path_t* p_beginning_2 = p; dir_t direction_1 = DIR_U; dir_t direction_2 = (*p & PATH_MASK_ARCS) ? DIR_D : DIR_L; path_direction(p, &direction_1); path_direction(p, &direction_2); direction_tmp = path_connected_to(*p, direction_1); path_follow(&p_beginning_1, &direction_tmp); direction_tmp = path_connected_to(*p, direction_2); path_follow(&p_beginning_2, &direction_tmp); if (p_beginning_1 == p_beginning_2) { uint16_t distance_1 = path_length_from(p, direction_1); uint16_t distance_2 = path_length_from(p, direction_2); *direction_forward_1 = distance_1 < distance_2 ? direction_1 : direction_2; *direction_forward_2 = DIR_0; } else { *direction_forward_1 = direction_1; *direction_forward_2 = direction_2; } } else { dir_t direction = DIR_0; for (direction_tmp = DIR_U; direction_tmp <= DIR_D; ++direction_tmp) { direction = direction_tmp; path_direction(p, &direction); if (direction) break; } *direction_forward_1 = direction; *direction_forward_2 = DIR_0; } } static uint8_t paths_ending_at(path_t* p, dir_t directions[]) { uint8_t directions_count = 0; dir_t direction; for (direction = DIR_U; direction <= DIR_D; ++direction) { if (!direction_occupied(*p, direction)) { path_t* p_next = path_next_to(p, direction); if (direction_occupied(*p_next, DIR_OPPOSITE(direction))) { directions[directions_count++] = direction; } } } for (direction = directions_count; direction < 4; ++direction) { directions[direction] = DIR_0; } return directions_count; } void paths_clear(void) { memset(&paths_connections[0], 0, 256); paths_cursor_x = (11 - 1) / 2 + 1; paths_cursor_y = (11 - 1) / 2 + 1; paths_cursor_col = COLOR_NONE; cursor_pressed = 0; cursor_link_from_side = 0; cursor_link_choice_next = 0; } static bool cursor_allowed(uint8_t new_i, uint8_t direction) { return ( ( (cells_ground_type[new_i] == GROUND_ROAD && (cells_ground_links[new_i] & direction_to_code[DIR_OPPOSITE(direction)])) || (cells_ground_type[new_i] >= GROUND_GRASS && paths_cursor_col == COLOR_PURPLE) || (global_date == 4*31+1) ) ) && (cells_item_type[new_i] != ITEM_HOUSE); } static bool cursor_move(uint8_t direction, bool new_pressed) { path_t* p = &paths_connections[(paths_cursor_y*16)+paths_cursor_x]; uint8_t new_x = paths_cursor_x; uint8_t new_y = paths_cursor_y; uint8_t new_i; path_t* new_p; dir_t direction_back = direction ? DIR_OPPOSITE(direction) : DIR_0; uint8_t new_link_choice_next = 0; new_i = coords_move(direction, &new_x, &new_y); if ((uint8_t)(new_x - 1) >= 11) goto cancel; if ((uint8_t)(new_y - 1) >= 11) goto cancel; new_p = &paths_connections[new_i]; if (new_pressed) { if (!cursor_pressed) { path_t* new_beg = new_p; dir_t directions_ending[4]; uint8_t directions_ending_count = paths_ending_at(new_p, &directions_ending); if (directions_ending_count) { cursor_link_from_side = directions_ending[cursor_link_choice_next]; new_link_choice_next = (cursor_link_choice_next + 1) % directions_ending_count; } else if (cells_item_type[new_i] == ITEM_TRUCK) { if (*new_p & PATH_MASK_DIRS) { path_clear_from(new_p, paths_direction(new_p)); } cursor_link_from_side = DIR_0; goto beginning_is_here; } else if (*new_p & PATH_MASK_WAYS) { dir_t direction_forward, direction_forward_2; path_directions(new_p, &direction_forward, &direction_forward_2); assert(direction_forward != DIR_0); cursor_link_from_side = path_connected_to(*new_p, direction_forward); path_clear_from(new_p, direction_forward); } else { goto cancel; } #define ByValue(var) (directions_ending_count = var, &directions_ending_count) path_follow(&new_beg, ByValue(cursor_link_from_side)); #undef ByValue beginning_is_here: paths_cursor_col = cells_item_color[new_beg - (&paths_connections[0])]; } else if (!cursor_link_from_side) { if (!cursor_allowed(new_i, direction)) goto cancel; if (!path_direction_add(p, direction)) goto cancel; cursor_link_from_side = direction_back; } else if (direction == cursor_link_from_side) { dir_t linked_to = path_connected_to(*new_p, direction_back); bool res; if (linked_to) { res = paths_disconnect(new_p, linked_to, direction_back); assert(res); } else { DIRECTION_REMOVE(new_p, direction_back); } cursor_link_from_side = linked_to; } else { if (!cursor_allowed(new_i, direction)) goto cancel; if (!paths_connect(p, cursor_link_from_side, direction)) goto cancel; cursor_link_from_side = direction_back; } } else { if (cursor_pressed) new_link_choice_next = cursor_link_choice_next; paths_cursor_col = COLOR_NONE; } paths_cursor_x = new_x; paths_cursor_y = new_y; cursor_pressed = new_pressed; cursor_link_choice_next = new_link_choice_next; return true; cancel: return false; } bool paths_cursor(uint8_t direction, bool pressed) { return ((direction || (pressed != cursor_pressed)) && cursor_move(direction, pressed)); } bool paths_alpha() { uint8_t i = (paths_cursor_y*16)+paths_cursor_x; if (cells_item_type[i] == ITEM_DROPPOINT) { cells_item_state[i] ^= 1; return true; } return false; } void paths_draw(void) { /* uint8_t y; for (y = 1; y <= 11; ++y) { uint8_t x; for (x = 1; x <= 11; ++x) { path_t* p = &paths_connections[y*16+x]; dir_t src; for (src = DIR_U; src <= DIR_D; ++src) { dir_t dst = path_connected_to(*p, src); draw_paths_color(COLOR_PURPLE); if (dst > src) { draw_paths_cell(x, y, src, dst); } if (*p & direction_to_code[src]) { draw_paths_cell(x, y, 0, src); } } } } */ /* uint8_t y; for (y = 1; y <= 11; ++y) { uint8_t x; for (x = 1; x <= 11; ++x) { path_t* p = &paths_connections[y*16+x]; if (*p & PATH_MASK_DIRS) { uint8_t px = x; uint8_t py = y; uint8_t index; dir_t direction_forward = paths_direction(p); dir_t direction_backward; assert(direction_forward != DIR_0); draw_paths_color(cells_item_color[y*16+x]); draw_paths_cell(x, y, 0, direction_forward); for (;;) { index = coords_move(direction_forward, &px, &py); direction_backward = DIR_OPPOSITE(direction_forward); direction_forward = path_connected_to(paths_connections[index], direction_backward); if (!direction_forward) break; global_delay++; draw_paths_cell(px, py, direction_backward, direction_forward); } draw_paths_cell(px, py, direction_backward, 0); } } } */ const struct truck* truck; uint8_t index; for (truck = &level_trucks[0], index = 0; index < level.count.trucks; ++truck, ++index) { uint8_t position = truck->position; const path_t* p = &paths_connections[position]; dir_t direction_forward = truck->directions_last_next & 0x0F; if (direction_forward != DIR_0) { uint8_t x = position & 0x0F; uint8_t y = position >> 4; dir_t direction_backward; draw_paths_color(truck->color); draw_paths_cell(x, y, 0, direction_forward); for (;;) { position = coords_move(direction_forward, &x, &y); direction_backward = DIR_OPPOSITE(direction_forward); direction_forward = path_connected_to(paths_connections[position], direction_backward); if (!direction_forward) break; global_delay++; draw_paths_cell(x, y, direction_backward, direction_forward); } draw_paths_cell(x, y, direction_backward, 0); } } }