File indexing completed on 2024-10-13 03:43:41
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"