File indexing completed on 2024-04-21 04:02:22

0001 /*
0002     SPDX-FileCopyrightText: 2009 Ian Wadham <iandw.au@gmail.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "kgrlevelgrid.h"
0008 
0009 KGrLevelGrid::KGrLevelGrid (QObject * parent, const KGrRecording * theLevelData)
0010     :
0011     QObject     (parent)
0012 {
0013     // Put a concrete wall all round the layout: left, right, top and bottom.
0014     // This saves ever having to test for being at the edge of the layout.
0015     int inWidth      = theLevelData->width;
0016     width            = inWidth + ConcreteWall * 2;
0017 
0018     int inHeight     = theLevelData->height;
0019     height           = inHeight + ConcreteWall * 2;
0020 
0021     int size         = width * height;
0022 
0023     layout.fill      (CONCRETE, size);
0024 
0025     // Initialise the flags for each cell.
0026     heroAccess.fill  (0, size);
0027     enemyAccess.fill (0, size);
0028     enemyHere.fill   (-1, size);
0029 
0030     // Copy the cells of the layout, but enclosed within the concrete wall.
0031     int inRow  = 0;
0032     int outRow = width + ConcreteWall;
0033 
0034     for (int j = 0; j < inHeight; j++) {
0035         for (int i = 0; i < inWidth; i++) {
0036             char type = theLevelData->layout [inRow + i];
0037             switch (type) {
0038             case HLADDER:
0039                 // Change hidden ladders to FREE, but keep a list of them.
0040                 hiddenLadders.append (outRow + i);
0041                 type = FREE;
0042                 break;
0043             case HENEMY:
0044                 // Change hidden enemies to BRICK, but keep a list of them.
0045                 hiddenEnemies.append (outRow + i);
0046                 type = BRICK;
0047                 break;
0048             case FLASHING:
0049                 // Change flashing nuggets to NUGGET, but keep a list of them.
0050                 flashingGold.append (outRow + i);
0051                 type = NUGGET;
0052                 break;
0053             }
0054             layout [outRow + i] = type;
0055         }
0056         inRow  = inRow  + inWidth;
0057         outRow = outRow + width;
0058     }
0059 }
0060 
0061 KGrLevelGrid::~KGrLevelGrid()
0062 {
0063 }
0064 
0065 // Inline functions (see kgrlevelgrid.h).
0066 //     char cellType   (int i, int j)
0067 //     char heroMoves  (int i, int j)
0068 //     char enemyMoves (int i, int j)
0069 //     void gotGold    (const int i, const int j, const bool runnerHasGold)
0070 //
0071 //     void setEnemyOccupied (int i, int j, const int spriteId)
0072 //     int enemyOccupied (int i, int j)
0073 //
0074 //     int  index      (int i, int j)
0075 
0076 void KGrLevelGrid::calculateAccess (bool pRunThruHole)
0077 {
0078     runThruHole = pRunThruHole;     // Save a copy of the runThruHole rule.
0079 
0080     char here;
0081     bool canEnter;
0082 
0083     // Calculate which cells can be entered (N.B. USEDHOLE is a trapped enemy).
0084     for (int j = 1; j < height - 1; j++) {
0085         for (int i = 1; i < width - 1; i++) {
0086             here      = cellType (i, j);
0087             canEnter  = (here != BRICK)  && (here != CONCRETE) &&
0088                         (here != FBRICK) && (here != USEDHOLE);
0089             heroAccess  [index (i, j)] = canEnter ? ENTERABLE : 0;
0090             enemyAccess [index (i, j)] = canEnter ? ENTERABLE : 0;
0091         }
0092     }
0093 
0094     // Calculate the access *from* each cell to its neighbours.
0095     for (int j = 1; j < height - 1; j++) {
0096         for (int i = 1; i < width - 1; i++) {
0097             calculateCellAccess (i, j);
0098         }
0099     }
0100 }
0101 
0102 void KGrLevelGrid::changeCellAt (const int i, const int j, const char type)
0103 {
0104     int  position          = index (i, j);
0105     bool canEnter          = (type != BRICK) && (type != CONCRETE) &&
0106                              (type != FBRICK) && (type != USEDHOLE);
0107     layout      [position] = type;
0108     heroAccess  [position] = canEnter ? ENTERABLE : 0;
0109     enemyAccess [position] = canEnter ? ENTERABLE : 0;
0110 
0111     calculateCellAccess (i, j);     // Recalculate access *from* this cell
0112                     // and access *to* it
0113     calculateCellAccess (i, j - 1); // from above,
0114     calculateCellAccess (i - 1, j); // from left,
0115     calculateCellAccess (i + 1, j); // from right,
0116     calculateCellAccess (i, j + 1); // and from below.
0117 }
0118 
0119 void KGrLevelGrid::calculateCellAccess (const int i, const int j)
0120 {
0121     Flags access = 0;
0122     char  here   = cellType (i, j);
0123     if (here == CONCRETE) {
0124         // If edge-cell or other CONCRETE, no calculation (avoid index errors).
0125         return;
0126     }
0127     char  below  = cellType (i, j + 1);
0128 
0129     access = heroMoves (i, j) & ENTERABLE;
0130 
0131     // Cannot enter brick, concrete or used hole: can drop into a false brick.
0132     if (! (access & ENTERABLE) && (here != FBRICK)) {
0133     access = 0;
0134     }
0135     // If can stand or hang on anything, allow down, left and right.
0136     else if ((below == BRICK) || (below == CONCRETE) || (below == USEDHOLE) ||
0137     (below == LADDER) || (here == LADDER) || (here == BAR)) {
0138     access |= (dFlag [STAND] | dFlag [DOWN] |
0139            dFlag [LEFT]  | dFlag [RIGHT]);
0140     }
0141     // If cannot stand or hang, can go down (space or false brick) or
0142     // maybe left or right (when standing on an enemy).
0143     else {
0144     access |= (dFlag [DOWN] | dFlag [LEFT]  | dFlag [RIGHT]);
0145     }
0146     // Can only go up if there is a ladder here.
0147     if (here == LADDER) {
0148     access |= dFlag [UP];
0149     }
0150 
0151     // Mask out directions that are blocked above, below, L or R, but not for
0152     // concrete/brick at edge of grid (or elsewhere) to avoid indexing errors.
0153     if (access != 0) {
0154         if (! (heroMoves (i, j - 1) & ENTERABLE)) {
0155             access = ~dFlag [UP] & access;      // Cannot go up.
0156         }
0157         if (! (heroMoves (i - 1, j) & ENTERABLE)) {
0158             access = ~dFlag [LEFT] & access;        // Cannot go left.
0159         }
0160         if (! (heroMoves (i + 1, j) & ENTERABLE)) {
0161             access = ~dFlag [RIGHT] & access;       // Cannot go right.
0162         }
0163         if (! (heroMoves (i, j + 1) & ENTERABLE)) {
0164             if (below != FBRICK) {
0165                 access = ~dFlag [DOWN] & access;    // Cannot go down.
0166             }
0167         }
0168     }
0169 
0170     heroAccess [index (i, j)]  = access;
0171     
0172     // Enemy access is the same as the hero's when no holes are open.
0173     enemyAccess [index (i, j)] = heroAccess [index (i, j)];
0174 
0175     if (here == USEDHOLE) {
0176         enemyAccess [index (i, j)] = UP;    // Can only climb out of hole.
0177     }
0178     else if (! runThruHole) {           // Check the rule.
0179         char mask;
0180         mask = (cellType (i - 1, j) == HOLE) ? dFlag [LEFT] : 0;
0181         mask = (cellType (i + 1, j) == HOLE) ? (dFlag [RIGHT] | mask) : mask;
0182         enemyAccess [index (i, j)] &= ~mask;    // Block access to holes at L/R.
0183     }
0184 }
0185 
0186 void KGrLevelGrid::placeHiddenLadders()
0187 {
0188     for (const int &offset : std::as_const(hiddenLadders)) {
0189         int i = offset % width;
0190         int j = offset / width;
0191         changeCellAt (i, j, LADDER);
0192     }
0193     Q_EMIT showHiddenLadders (hiddenLadders, width);
0194     hiddenLadders.clear();
0195 }
0196 
0197 #include "moc_kgrlevelgrid.cpp"