void PreviewScene::refresh(uint16_t (&playfield)[2][MAX_FIELD_HEIGHT][MAX_FIELD_WIDTH]) { // load the 3d tile resource // (NOTE: this is a temporary measure until actual graphic/palette // loading is implemented) // each palette zone is 216px tall and each row of tiles is // 32 tiles long. if (waterLevel(level)) tiles.load(":images/3dtiles-water.png"); else tiles.load(":images/3dtiles.png"); int mapHeight = levelHeight(level); int mapWidth = level->header.width; int mapLength = level->header.length; level->header.fieldHeight = 2 * (mapHeight + mapWidth + mapLength + 2); level->header.fieldWidth = 4 * (mapWidth + mapLength); int width = level->header.fieldWidth; int height = level->header.fieldHeight; // reset the scene (remove all members) this->clear(); // set the pixmap and scene size based on the playfield's size QPixmap pixmap(width * ISO_TILE_SIZE, height * ISO_TILE_SIZE); pixmap.fill(QColor(0, 0, 0, 0)); this->setSceneRect(0, 0, width * ISO_TILE_SIZE, height * ISO_TILE_SIZE); // no level area = don't render anything if (mapLength + mapWidth == 0) { this->addPixmap(pixmap); this->update(); return; } // used to render one tile w/ flip etc. for each layer QPixmap layer1tile(ISO_TILE_SIZE, ISO_TILE_SIZE); QPixmap layer2tile(ISO_TILE_SIZE, ISO_TILE_SIZE); QPainter layer1painter, layer2painter; QPainter painter; painter.begin(&pixmap); for (int h = 0; h < height; h++) { for (int w = 0; w < width; w++) { layer1tile.fill(QColor(0,0,0,0)); layer2tile.fill(QColor(0,0,0,0)); uint16_t tile1 = playfield[0][h][w]; if (TILE(tile1)) { int y1 = (TILE(tile1) / 32) * ISO_TILE_SIZE + (PALN(tile1) * 216); int x1 = (TILE(tile1) % 32) * ISO_TILE_SIZE; layer1painter.begin(&layer1tile); layer1painter.drawPixmap(0, 0, tiles, x1, y1, ISO_TILE_SIZE, ISO_TILE_SIZE); layer1painter.end(); // flip the pixmap horizontally and/or vertically layer1tile = QPixmap::fromImage(layer1tile.toImage().mirrored(tile1 & FH, tile1 & FV)); } uint16_t tile2 = playfield[1][h][w]; if (TILE(tile2)) { int y2 = (TILE(tile2) / 32) * ISO_TILE_SIZE + (PALN(tile2) * 216); int x2 = (TILE(tile2) % 32) * ISO_TILE_SIZE; layer2painter.begin(&layer2tile); layer2painter.drawPixmap(0, 0, tiles, x2, y2, ISO_TILE_SIZE, ISO_TILE_SIZE); layer2painter.end(); // flip the pixmap horizontally and/or vertically layer2tile = QPixmap::fromImage(layer2tile.toImage().mirrored(tile2 & FH, tile2 & FV)); } // draw the tile pixmaps onto the scene // if layer 1 has priority, draw layer 2 first if (tile1 & PRI) { painter.drawPixmap(w * ISO_TILE_SIZE, h * ISO_TILE_SIZE, layer2tile); painter.drawPixmap(w * ISO_TILE_SIZE, h * ISO_TILE_SIZE, layer1tile); } else { painter.drawPixmap(w * ISO_TILE_SIZE, h * ISO_TILE_SIZE, layer1tile); painter.drawPixmap(w * ISO_TILE_SIZE, h * ISO_TILE_SIZE, layer2tile); } } } // next, if sprites are enabled, draw them // most of this is copied from the 2D draw code if (sprites) { for (int y = 0; y < mapLength; y++) { for (int x = 0; x < mapWidth; x++) { QPixmap gfx; int frame; int obs = level->tiles[y][x].obstacle; int z = level->tiles[y][x].height; if (obs == 0) continue; // whispy woods (index 0x00 in enemies.png) if (obs == 0x02) { gfx = enemies; frame = 0; // kirby's start pos (kirby.png) // (this time also use the final boss version) } else if (obs == 0x0c || obs == 0xc3) { gfx = player; frame = 0; // dedede (frame 0 in dedede.png) } else if (obs == 0x0d) { gfx = dedede; frame = 0; // most enemies (ind. 01 to 13 in enemies.png) } else if (obs >= 0x40 && obs <= 0x52) { gfx = enemies; frame = obs - 0x40 + 1; // transformer (ind. 14 in enemies.png } else if (obs == 0x57) { gfx = enemies; frame = 0x14; // gordo (ind. 00 to 21 in gordo.png) } else if (obs >= 0x80 && obs <= 0x97) { gfx = gordo; frame = obs - 0x80; // kracko (index 15-17 in enemies.png) } else if (obs >= 0xac && obs <= 0xae) { gfx = enemies; frame = obs - 0xac + 0x15; // anything else - question mark (or don't draw) } else { obs = 0; } // draw the selected obstacle if (obs) { // horizontal: start at 0 pixels // move TILE_SIZE / 2 right for each positive move on the x-axis (west to east) // and TILE_SIZE / 2 left for each positive move on the y-axis (north to south) int startX = (TILE_SIZE / 2) * (x + (mapLength - y - 1)); // start at h * TILE_SIZE / 4 tiles // move TILE_SIZE / 4 down for each positive move on the x-axis (west to east) // and TILE_SIZE / 4 down for each positive move on the y-axis (north to south) // and TILE_SIZE / 4 up for each positive move on the z-axis (tile z) // and then adjust for height of sprites int startY = (TILE_SIZE / 4) * (mapHeight + x + y - z + 4) - gfx.height(); // move down half a tile's worth if the sprite is on a slope if (level->tiles[y][x].geometry >= stuff::slopes) startY += TILE_SIZE / 8; painter.drawPixmap(startX, startY, gfx, frame * TILE_SIZE, 0, TILE_SIZE, gfx.height()); } } } } // finally, add the 3d map pixmap onto the scene painter.end(); this->addPixmap(pixmap); this->update(); }
bool Mine::doCommand(RobotCommand command) { if (state != State::InProgress) return false; if (command == RobotCommand::Abort) { state = State::Aborted; commandHistory.push_back(command); return true; } int newRobotX = var.robotX; int newRobotY = var.robotY; std::vector<MineUpdate> updateQueue; int dx=0, dy=0; switch (command) { case RobotCommand::Left: dx=-1; break; case RobotCommand::Right: dx=+1; break; case RobotCommand::Up: dy=+1; break; case RobotCommand::Down: dy=-1; break; default: break; } if (dx != 0 || dy != 0) { int nx = var.robotX + dx; int ny = var.robotY + dy; auto c = get(nx, ny); if (c == Tile::Empty || c == Tile::Earth || c == Tile::Lambda || c == Tile::OpenLift || c == Tile::Razor) { newRobotX = nx; newRobotY = ny; } else if (rockType(c) && dy == 0 && get(nx + dx, var.robotY) == Tile::Empty) { updateQueue.push_back({nx+dx, var.robotY, c}); newRobotX = nx; } else if (c >= Tile::TrampolineA && c <= Tile::TrampolineI) { Tile target = problem->getTargetForTramp(c); Position jumpTo = problem->targetLoc[problem->indexOfTrampTarget(target)]; newRobotX = jumpTo.x; newRobotY = jumpTo.y; for (auto i : problem->getTrampLocsForTarget(target)) { updateQueue.push_back({i.x, i.y, Tile::Empty}); } } // If we have been commanded to move, but haven't moved, command must not // have been valid, do nothing. if (newRobotX == var.robotX && newRobotY == var.robotY) return false; } if (command == RobotCommand::Slice) { if (var.curRazors > 0) { var.curRazors--; for (auto i : BeardDirs) { if (get(var.robotX + i.dx, var.robotY + i.dy) == Tile::Beard) { updateQueue.push_back({var.robotX + i.dx, var.robotY + i.dy, Tile::Empty}); } } } else { return false; } } commandHistory.push_back(command); auto nr = get(newRobotX, newRobotY); if (nr == Tile::Lambda) ++var.collectedLambdas; else if (nr == Tile::Razor) ++var.curRazors; else if (nr == Tile::OpenLift) state = State::Win; updateQueue.push_back({var.robotX, var.robotY, Tile::Empty}); updateQueue.push_back({newRobotX, newRobotY, Tile::Robot}); // Apply all of the robot moves before computing the world update. for (auto update : updateQueue) set(update.x, update.y, update.c); updateQueue.clear(); var.robotX = newRobotX; var.robotY = newRobotY; bool beardGrowTime = (moveCount()+1) % problem->beard.growthrate == 0; for (auto const& rockbeard : rockBeardPositions) { int x = rockbeard.x; int y = rockbeard.y; if (rockType(get(rockbeard))) { Tile thisRock = get(rockbeard); if (get(x, y - 1) == Tile::Empty) { updateQueue.push_back({x, y, Tile::Empty}); if (thisRock == Tile::HigherOrderRock && get(x, y - 2) != Tile::Empty) { updateQueue.push_back({x, y - 1, Tile::Lambda}); } else { updateQueue.push_back({x, y - 1, thisRock}); } } else if (rockType(get(x, y - 1)) && get(x + 1, y) == Tile::Empty && get(x + 1, y - 1) == Tile::Empty) { updateQueue.push_back({x, y, Tile::Empty}); if (thisRock == Tile::HigherOrderRock && get(x + 1, y - 2) != Tile::Empty) { updateQueue.push_back({x + 1, y - 1, Tile::Lambda}); } else { updateQueue.push_back({x + 1, y - 1, thisRock}); } } else if (rockType(get(x, y - 1)) && get(x - 1, y) == Tile::Empty && get(x - 1, y - 1) == Tile::Empty) { updateQueue.push_back({x, y, Tile::Empty}); if (thisRock == Tile::HigherOrderRock && get(x - 1, y - 2) != Tile::Empty) { updateQueue.push_back({x - 1, y - 1, Tile::Lambda}); } else { updateQueue.push_back({x - 1, y - 1, thisRock}); } } else if (get(x, y - 1) == Tile::Lambda && get(x + 1, y) == Tile::Empty && get(x + 1, y - 1) == Tile::Empty) { updateQueue.push_back({x, y, Tile::Empty}); if (thisRock == Tile::HigherOrderRock && get(x + 1, y - 2) != Tile::Empty) { updateQueue.push_back({x + 1, y - 1, Tile::Lambda}); } else { updateQueue.push_back({x + 1, y - 1, thisRock}); } } } else { assert(get(rockbeard) == Tile::Beard); if (beardGrowTime) { for ( auto dir : BeardDirs ) { int nx = x + dir.dx; int ny = y + dir.dy; if ( get(nx, ny) == Tile::Empty ) { updateQueue.push_back({nx, ny, Tile::Beard}); } } } } } if (var.curWaterLevel <= var.robotY) var.submergedSteps = 0; var.curWaterLevel = waterLevel(totalMoves + 1); if (var.curWaterLevel > var.robotY) var.submergedSteps += 1; if (var.submergedSteps > problem->water.waterproof) { state = State::Lose; } if (var.collectedLambdas == problem->numInitialLambdas) { for (auto lift : problem->liftLoc) updateQueue.push_back({lift.x, lift.y, Tile::OpenLift}); } for (auto update : updateQueue) { if ((rockType(update.c) || update.c == Tile::Lambda) && update.x == var.robotX && update.y == var.robotY + 1) { // Robot was hit on the head by rock state = State::Lose; } set(update.x, update.y, update.c); } ++totalMoves; return true; }