1 /*
2 SPDX-FileCopyrightText: 2009 Ian Wadham <iandw.au@gmail.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include "kgrlevelgrid.h"
8
KGrLevelGrid(QObject * parent,const KGrRecording * theLevelData)9 KGrLevelGrid::KGrLevelGrid (QObject * parent, const KGrRecording * theLevelData)
10 :
11 QObject (parent)
12 {
13 // Put a concrete wall all round the layout: left, right, top and bottom.
14 // This saves ever having to test for being at the edge of the layout.
15 int inWidth = theLevelData->width;
16 width = inWidth + ConcreteWall * 2;
17
18 int inHeight = theLevelData->height;
19 height = inHeight + ConcreteWall * 2;
20
21 int size = width * height;
22
23 layout.fill (CONCRETE, size);
24
25 // Initialise the flags for each cell.
26 heroAccess.fill (0, size);
27 enemyAccess.fill (0, size);
28 enemyHere.fill (-1, size);
29
30 // Copy the cells of the layout, but enclosed within the concrete wall.
31 int inRow = 0;
32 int outRow = width + ConcreteWall;
33
34 for (int j = 0; j < inHeight; j++) {
35 for (int i = 0; i < inWidth; i++) {
36 char type = theLevelData->layout [inRow + i];
37 switch (type) {
38 case HLADDER:
39 // Change hidden ladders to FREE, but keep a list of them.
40 hiddenLadders.append (outRow + i);
41 type = FREE;
42 break;
43 case HENEMY:
44 // Change hidden enemies to BRICK, but keep a list of them.
45 hiddenEnemies.append (outRow + i);
46 type = BRICK;
47 break;
48 case FLASHING:
49 // Change flashing nuggets to NUGGET, but keep a list of them.
50 flashingGold.append (outRow + i);
51 type = NUGGET;
52 break;
53 }
54 layout [outRow + i] = type;
55 }
56 inRow = inRow + inWidth;
57 outRow = outRow + width;
58 }
59 }
60
~KGrLevelGrid()61 KGrLevelGrid::~KGrLevelGrid()
62 {
63 }
64
65 // Inline functions (see kgrlevelgrid.h).
66 // char cellType (int i, int j)
67 // char heroMoves (int i, int j)
68 // char enemyMoves (int i, int j)
69 // void gotGold (const int i, const int j, const bool runnerHasGold)
70 //
71 // void setEnemyOccupied (int i, int j, const int spriteId)
72 // int enemyOccupied (int i, int j)
73 //
74 // int index (int i, int j)
75
calculateAccess(bool pRunThruHole)76 void KGrLevelGrid::calculateAccess (bool pRunThruHole)
77 {
78 runThruHole = pRunThruHole; // Save a copy of the runThruHole rule.
79
80 char here;
81 bool canEnter;
82
83 // Calculate which cells can be entered (N.B. USEDHOLE is a trapped enemy).
84 for (int j = 1; j < height - 1; j++) {
85 for (int i = 1; i < width - 1; i++) {
86 here = cellType (i, j);
87 canEnter = (here != BRICK) && (here != CONCRETE) &&
88 (here != FBRICK) && (here != USEDHOLE);
89 heroAccess [index (i, j)] = canEnter ? ENTERABLE : 0;
90 enemyAccess [index (i, j)] = canEnter ? ENTERABLE : 0;
91 }
92 }
93
94 // Calculate the access *from* each cell to its neighbours.
95 for (int j = 1; j < height - 1; j++) {
96 for (int i = 1; i < width - 1; i++) {
97 calculateCellAccess (i, j);
98 }
99 }
100 }
101
changeCellAt(const int i,const int j,const char type)102 void KGrLevelGrid::changeCellAt (const int i, const int j, const char type)
103 {
104 int position = index (i, j);
105 bool canEnter = (type != BRICK) && (type != CONCRETE) &&
106 (type != FBRICK) && (type != USEDHOLE);
107 layout [position] = type;
108 heroAccess [position] = canEnter ? ENTERABLE : 0;
109 enemyAccess [position] = canEnter ? ENTERABLE : 0;
110
111 calculateCellAccess (i, j); // Recalculate access *from* this cell
112 // and access *to* it
113 calculateCellAccess (i, j - 1); // from above,
114 calculateCellAccess (i - 1, j); // from left,
115 calculateCellAccess (i + 1, j); // from right,
116 calculateCellAccess (i, j + 1); // and from below.
117 }
118
calculateCellAccess(const int i,const int j)119 void KGrLevelGrid::calculateCellAccess (const int i, const int j)
120 {
121 Flags access = 0;
122 char here = cellType (i, j);
123 if (here == CONCRETE) {
124 // If edge-cell or other CONCRETE, no calculation (avoid index errors).
125 return;
126 }
127 char below = cellType (i, j + 1);
128
129 access = heroMoves (i, j) & ENTERABLE;
130
131 // Cannot enter brick, concrete or used hole: can drop into a false brick.
132 if (! (access & ENTERABLE) && (here != FBRICK)) {
133 access = 0;
134 }
135 // If can stand or hang on anything, allow down, left and right.
136 else if ((below == BRICK) || (below == CONCRETE) || (below == USEDHOLE) ||
137 (below == LADDER) || (here == LADDER) || (here == BAR)) {
138 access |= (dFlag [STAND] | dFlag [DOWN] |
139 dFlag [LEFT] | dFlag [RIGHT]);
140 }
141 // If cannot stand or hang, can go down (space or false brick) or
142 // maybe left or right (when standing on an enemy).
143 else {
144 access |= (dFlag [DOWN] | dFlag [LEFT] | dFlag [RIGHT]);
145 }
146 // Can only go up if there is a ladder here.
147 if (here == LADDER) {
148 access |= dFlag [UP];
149 }
150
151 // Mask out directions that are blocked above, below, L or R, but not for
152 // concrete/brick at edge of grid (or elsewhere) to avoid indexing errors.
153 if (access != 0) {
154 if (! (heroMoves (i, j - 1) & ENTERABLE)) {
155 access = ~dFlag [UP] & access; // Cannot go up.
156 }
157 if (! (heroMoves (i - 1, j) & ENTERABLE)) {
158 access = ~dFlag [LEFT] & access; // Cannot go left.
159 }
160 if (! (heroMoves (i + 1, j) & ENTERABLE)) {
161 access = ~dFlag [RIGHT] & access; // Cannot go right.
162 }
163 if (! (heroMoves (i, j + 1) & ENTERABLE)) {
164 if (below != FBRICK) {
165 access = ~dFlag [DOWN] & access; // Cannot go down.
166 }
167 }
168 }
169
170 heroAccess [index (i, j)] = access;
171
172 // Enemy access is the same as the hero's when no holes are open.
173 enemyAccess [index (i, j)] = heroAccess [index (i, j)];
174
175 if (here == USEDHOLE) {
176 enemyAccess [index (i, j)] = UP; // Can only climb out of hole.
177 }
178 else if (! runThruHole) { // Check the rule.
179 char mask;
180 mask = (cellType (i - 1, j) == HOLE) ? dFlag [LEFT] : 0;
181 mask = (cellType (i + 1, j) == HOLE) ? (dFlag [RIGHT] | mask) : mask;
182 enemyAccess [index (i, j)] &= ~mask; // Block access to holes at L/R.
183 }
184 }
185
placeHiddenLadders()186 void KGrLevelGrid::placeHiddenLadders()
187 {
188 for (const int &offset : std::as_const(hiddenLadders)) {
189 int i = offset % width;
190 int j = offset / width;
191 changeCellAt (i, j, LADDER);
192 }
193 Q_EMIT showHiddenLadders (hiddenLadders, width);
194 hiddenLadders.clear();
195 }
196
197
198