#include "Panel.h" #include "Random.h" #include "Memory.h" #include "Randomizer.h" #include template int find(const std::vector &data, T search, size_t startIndex = 0) { for (size_t i=startIndex ; i(i); } return -1; } Panel::Panel(int id) { _memory = std::make_shared("witness64_d3d11.exe"); _width = 2 * _memory->ReadPanelData(id, GRID_SIZE_X, 1)[0] - 1; _height = 2 * _memory->ReadPanelData(id, GRID_SIZE_Y, 1)[0] - 1; _grid.resize(_width); for (auto& row : _grid) row.resize(_height); ReadIntersections(id); ReadDecorations(id); } void Panel::Write(int id) { WriteIntersections(id); WriteDecorations(id); _memory->WritePanelData(id, GRID_SIZE_X, {(_width + 1)/2}); _memory->WritePanelData(id, GRID_SIZE_Y, {(_height + 1)/2}); } nlohmann::json Panel::Serialize() { nlohmann::json puzzle = { {"pillar", false}, {"dots", nlohmann::json::array()}, {"gaps", nlohmann::json::array()}, {"name", "Imported from The Witness :O"}, {"regionCache", nlohmann::json::object()}, }; if (_grid.empty()) return {}; puzzle["grid"] = nlohmann::json::array(); for (int x=0; x<_width; x++) { for (int y=0; y<_height; y++) { if (x%2 == 1 && y%2 == 1) { puzzle["grid"][x][y] = Decoration::to_json(_grid[x][y]); } else { if (_grid[x][y] & IntersectionFlags::HAS_DOT) { puzzle["dots"].emplace_back(nlohmann::json({{"x", x}, {"y", y}})); } puzzle["grid"][x][y] = false; } } } puzzle["startPoints"] = nlohmann::json::array(); for (auto [x, y] : _startpoints) { nlohmann::json startPoint = {{"x", x}, {"y", y}}; puzzle["startPoints"].emplace_back(startPoint); } puzzle["endPoints"] = nlohmann::json::array(); for (Endpoint endpoint : _endpoints) { puzzle["endPoints"].emplace_back(endpoint.to_json()); } std::string out = puzzle.dump(); return puzzle; } void Panel::Random() { /* for (auto& row : _decorations) { for (auto& cell : row) { cell.SetShape(cell.GetShape() & 0xFFFFFFF0); cell.SetShape(cell.GetShape() | Random::RandInt(1, 10)); } } */ } void Panel::ReadDecorations(int id) { int numDecorations = _memory->ReadPanelData(id, NUM_DECORATIONS, 1)[0]; std::vector decorations = _memory->ReadArray(id, DECORATIONS, numDecorations); for (int i=0; i decorations; for (int y=_height-2; y>0; y-=2) { for (int x=1; x<_width - 1; x+=2) { decorations.push_back(_grid[x][y]); } } _memory->WritePanelData(id, NUM_DECORATIONS, {static_cast(decorations.size())}); _memory->WriteArray(id, DECORATIONS, decorations); } void Panel::ReadIntersections(int id) { int numIntersections = _memory->ReadPanelData(id, NUM_DOTS, 1)[0]; std::vector intersectionFlags = _memory->ReadArray(id, DOT_FLAGS, numIntersections); int i = 0; for (;; i++) { auto [x, y] = loc_to_xy(i); if (y < 0) break; if (intersectionFlags[i] & IntersectionFlags::IS_STARTPOINT) { _startpoints.push_back({x, y}); } } int numConnections = _memory->ReadPanelData(id, NUM_CONNECTIONS, 1)[0]; std::vector connections_a = _memory->ReadArray(id, DOT_CONNECTION_A, numConnections); std::vector connections_b = _memory->ReadArray(id, DOT_CONNECTION_B, numConnections); std::vector intersections = _memory->ReadArray(id, DOT_POSITIONS, numIntersections*2); // Iterate the remaining intersections (endpoints, dots, gaps) for (; i < numIntersections; i++) { if (intersectionFlags[i] & IntersectionFlags::IS_ENDPOINT) { for (int j=0; j intersections[2*location]) { dir = Endpoint::Direction::RIGHT; } else if (intersections[2*i + 1] > intersections[2*location + 1]) { // y coordinate is 0 (bottom) 1 (top), so this check is reversed. dir = Endpoint::Direction::UP; } else { dir = Endpoint::Direction::DOWN; } auto [x, y] = loc_to_xy(location); _endpoints.push_back(Endpoint(x, y, dir)); break; } } } else if (intersectionFlags[i] & IntersectionFlags::HAS_DOT) { for (int j=0; j intersections[2*location]) { // To the right x++; } else if (intersections[2*i + 1] > intersections[2*location + 1]) { // y coordinate is 0 (bottom) 1 (top), so this check is reversed. We are above the target (location) y--; } else { // Beleow the target y++; } _grid[x][y] |= IntersectionFlags::HAS_DOT; break; } } } } } void Panel::WriteIntersections(int id) { std::vector intersections; std::vector intersectionFlags; std::vector connections_a; std::vector connections_b; double min = 0.1; double max = 0.9; double width_interval = (max - min) / (_width/2); double height_interval = (max - min) / (_height/2); // TODO(future): Stop using push_back and set these into explicit locations, unrequires loop iteration order for (int y=_height-1; y>=0; y-=2) { for (int x=0; x<_width; x+=2) { intersections.push_back(static_cast(min + (x/2) * width_interval)); intersections.push_back(static_cast(max - (y/2) * height_interval)); int flags = 0; if (find(_startpoints, {x, y}) != -1) flags |= IntersectionFlags::IS_STARTPOINT; intersectionFlags.push_back(flags); // Create connections for this intersection -- always write low -> high if (y > 0) { connections_a.push_back(xy_to_loc(x, y-2)); connections_b.push_back(xy_to_loc(x, y)); } if (x > 0) { connections_a.push_back(xy_to_loc(x-2, y)); connections_b.push_back(xy_to_loc(x, y)); } } } for (Endpoint endpoint : _endpoints) { connections_a.push_back(xy_to_loc(endpoint.GetX(), endpoint.GetY())); // Target to connect to connections_b.push_back(static_cast(intersectionFlags.size())); // This endpoint double xPos = min + (endpoint.GetX()/2) * width_interval; double yPos = max - (endpoint.GetY()/2) * height_interval; if (endpoint.GetDir()== Endpoint::Direction::LEFT) { xPos -= .05; } else if (endpoint.GetDir() == Endpoint::Direction::RIGHT) { xPos += .05; } else if (endpoint.GetDir() == Endpoint::Direction::UP) { yPos += .05; // Y position goes from 0 (bottom) to 1 (top), so this is reversed. } else if (endpoint.GetDir() == Endpoint::Direction::DOWN) { yPos -= .05; } intersections.push_back(static_cast(xPos)); intersections.push_back(static_cast(yPos)); intersectionFlags.push_back(IntersectionFlags::IS_ENDPOINT); } // Dots for (int y=0; y<_height; y++) { for (int x=0; x<_width; x++) { if (!(_grid[x][y] & IntersectionFlags::HAS_DOT)) continue; if (x%2 == 1 && y%2 == 1) continue; if (x%2 == 0 && y%2 == 0) { intersectionFlags[xy_to_loc(x, y)] |= _grid[x][y]; continue; } // Locate the segment we're breaking for (int i=0; i(intersectionFlags.size()); // This endpoint connections_a.push_back(static_cast(intersectionFlags.size())); // This endpoint connections_b.push_back(other_connection); break; } } // Add this dot to the end double xPos = min + (x/2.0) * width_interval; double yPos = max - (y/2.0) * height_interval; intersections.push_back(static_cast(xPos)); intersections.push_back(static_cast(yPos)); intersectionFlags.push_back(_grid[x][y]); } } _memory->WritePanelData(id, NUM_DOTS, {static_cast(intersectionFlags.size())}); _memory->WriteArray(id, DOT_POSITIONS, intersections); _memory->WriteArray(id, DOT_FLAGS, intersectionFlags); _memory->WritePanelData(id, NUM_CONNECTIONS, {static_cast(connections_a.size())}); _memory->WriteArray(id, DOT_CONNECTION_A, connections_a); _memory->WriteArray(id, DOT_CONNECTION_B, connections_b); _memory->WritePanelData(id, NEEDS_REDRAW, {1}); }