Skip to content

Commit 5e346fa

Browse files
Maze cleanup
1 parent f5c563d commit 5e346fa

1 file changed

Lines changed: 19 additions & 195 deletions

File tree

src/Gamemodes/Maze.ts

Lines changed: 19 additions & 195 deletions
Original file line numberDiff line numberDiff line change
@@ -15,10 +15,9 @@
1515
You should have received a copy of the GNU Affero General Public License
1616
along with this program. If not, see <https://www.gnu.org/licenses/>
1717
*/
18-
import ArenaEntity, { ArenaState } from "../Native/Arena";
18+
import ArenaEntity from "../Native/Arena";
1919
import GameServer from "../Game";
20-
import MazeWall from "../Entity/Misc/MazeWall";
21-
import { VectorAbstract } from "../Physics/Vector";
20+
import MazeGenerator, { MazeGeneratorConfig } from "../Systems/MazeGenerator";
2221

2322
import ShapeManager from "../Entity/Shape/Manager";
2423

@@ -33,215 +32,40 @@ export class MazeShapeManager extends ShapeManager {
3332
3433
return Math.floor(12.5 * ratio);
3534
*/
36-
35+
3736
return 1300;
3837
}
3938
}
4039

41-
// constss.
42-
const CELL_SIZE = 635;
43-
const GRID_SIZE = 40;
44-
const ARENA_SIZE = CELL_SIZE * GRID_SIZE;
45-
const SEED_AMOUNT = Math.floor(Math.random() * 30) + 30;
46-
const TURN_CHANCE = 0.2;
47-
const BRANCH_CHANCE = 0.2;
48-
const TERMINATION_CHANCE = 0.2;
40+
const config: MazeGeneratorConfig = {
41+
CELL_SIZE: 635,
42+
GRID_SIZE: 40,
43+
SEED_AMOUNT: Math.floor(Math.random() * 30) + 30,
44+
TURN_CHANCE: 0.2,
45+
BRANCH_CHANCE: 0.2,
46+
TERMINATION_CHANCE: 0.2
47+
}
4948

50-
/**
51-
* Maze Gamemode Arena
52-
*
53-
* Implementation details:
54-
* Maze map generator by damocles <github.com/SpanksMcYeet>
55-
* - Added into codebase on December 3rd 2022
56-
*/
5749
export default class MazeArena extends ArenaEntity {
5850
static override GAMEMODE_ID: string = "maze";
5951

6052
protected shapes: ShapeManager = new MazeShapeManager(this);
6153

62-
/** Stores all the "seed"s */
63-
private SEEDS: VectorAbstract[] = [];
64-
/** Stores all the "wall"s, contains cell based coords */
65-
private WALLS: (VectorAbstract & {width: number, height: number})[] = [];
66-
/** Rolled out matrix of the grid */
67-
private MAZE: Uint8Array = new Uint8Array(GRID_SIZE * GRID_SIZE);
54+
public mazeGenerator: MazeGenerator = new MazeGenerator(this, config);
6855

6956
public constructor(game: GameServer) {
7057
super(game);
71-
this.updateBounds(ARENA_SIZE, ARENA_SIZE);
58+
59+
const arenaSize = config.CELL_SIZE * config.GRID_SIZE
60+
this.updateBounds(arenaSize, arenaSize);
61+
62+
this.mazeGenerator.buildMaze();
63+
7264
this.allowBoss = false;
73-
this._buildMaze();
74-
}
75-
/** Creates a maze wall from cell coords */
76-
private _buildWallFromGridCoord(gridX: number, gridY: number, gridW: number, gridH: number) {
77-
const scaledW = gridW * CELL_SIZE;
78-
const scaledH = gridH * CELL_SIZE;
79-
const scaledX = gridX * CELL_SIZE - ARENA_SIZE / 2 + (scaledW / 2);
80-
const scaledY = gridY * CELL_SIZE - ARENA_SIZE / 2 + (scaledH / 2);
81-
new MazeWall(this.game, scaledX, scaledY, scaledH, scaledW);
82-
}
83-
/** Allows for easier (x, y) based getting of maze cells */
84-
private _get(x: number, y: number): number {
85-
return this.MAZE[y * GRID_SIZE + x];
86-
}
87-
/** Allows for easier (x, y) based setting of maze cells */
88-
private _set(x: number, y: number, value: number): number {
89-
return this.MAZE[y * GRID_SIZE + x] = value;
90-
}
91-
/** Converts MAZE grid into an array of set and unset bits for ease of use */
92-
private _mapValues(): [x: number, y: number, value: number][] {
93-
const values: [x: number, y: number, value: number][] = Array(this.MAZE.length);
94-
for (let i = 0; i < this.MAZE.length; ++i) values[i] = [i % GRID_SIZE, Math.floor(i / GRID_SIZE), this.MAZE[i]];
95-
return values;
96-
}
97-
/** Builds the maze */
98-
protected _buildMaze() {
99-
// Plant some seeds
100-
for (let i = 0; i < 10000; i++) {
101-
// Stop if we exceed our maximum seed amount
102-
if (this.SEEDS.length >= SEED_AMOUNT) break;
103-
// Attempt a seed planting
104-
let seed: VectorAbstract = {
105-
x: Math.floor((Math.random() * GRID_SIZE) - 1),
106-
y: Math.floor((Math.random() * GRID_SIZE) - 1),
107-
};
108-
// Check if our seed is valid (is 3 GU away from another seed, and is not on the border)
109-
if (this.SEEDS.some(a => (Math.abs(seed.x - a.x) <= 3 && Math.abs(seed.y - a.y) <= 3))) continue;
110-
if (seed.x <= 0 || seed.y <= 0 || seed.x >= GRID_SIZE - 1 || seed.y >= GRID_SIZE - 1) continue;
111-
// Push it to the pending seeds and set its grid to a wall cell
112-
this.SEEDS.push(seed);
113-
this._set(seed.x, seed.y, 1);
114-
}
115-
const direction: number[][] = [
116-
[-1, 0], [1, 0], // left and right
117-
[0, -1], [0, 1], // up and down
118-
];
119-
// Let it grow!
120-
for (let seed of this.SEEDS) {
121-
// Select a direction we want to head in
122-
let dir: number[] = direction[Math.floor(Math.random() * 4)];
123-
let termination = 1;
124-
// Now we can start to grow
125-
while (termination >= TERMINATION_CHANCE) {
126-
// Choose the next termination chance
127-
termination = Math.random();
128-
// Get the direction we're going in
129-
let [x, y] = dir;
130-
// Move forward in that direction, and set that grid to a wall cell
131-
seed.x += x;
132-
seed.y += y;
133-
if (seed.x <= 0 || seed.y <= 0 || seed.x >= GRID_SIZE - 1 || seed.y >= GRID_SIZE - 1) break;
134-
this._set(seed.x, seed.y, 1);
135-
// Now lets see if we want to branch or turn
136-
if (Math.random() <= BRANCH_CHANCE) {
137-
// If the seeds exceeds 75, then we're going to stop creating branches in order to avoid making a massive maze tumor(s)
138-
if (this.SEEDS.length > 75) continue;
139-
// Get which side we want the branch to be on (left or right if moving up or down, and up and down if moving left or right)
140-
let [ xx, yy ] = direction.filter(a => a.every((b, c) => b !== dir[c]))[Math.floor(Math.random() * 2)];
141-
// Create the seed
142-
let newSeed = {
143-
x: seed.x + xx,
144-
y: seed.y + yy,
145-
};
146-
// Push the seed and set its grid to a maze zone
147-
this.SEEDS.push(newSeed);
148-
this._set(seed.x, seed.y, 1);
149-
} else if (Math.random() <= TURN_CHANCE) {
150-
// Get which side we want to turn to (left or right if moving up or down, and up and down if moving left or right)
151-
dir = direction.filter(a => a.every((b, c) => b !== dir[c]))[Math.floor(Math.random() * 2)];
152-
}
153-
}
154-
}
155-
// Now lets attempt to add some singular walls around the arena
156-
for (let i = 0; i < 10; i++) {
157-
// Attempt to place it
158-
let seed = {
159-
x: Math.floor((Math.random() * GRID_SIZE) - 1),
160-
y: Math.floor((Math.random() * GRID_SIZE) - 1),
161-
};
162-
// Check if our sprinkle is valid (is 3 GU away from another wall, and is not on the border)
163-
if (this._mapValues().some(([x, y, r]) => r === 1 && (Math.abs(seed.x - x) <= 3 && Math.abs(seed.y - y) <= 3))) continue;
164-
if (seed.x <= 0 || seed.y <= 0 || seed.x >= GRID_SIZE - 1 || seed.y >= GRID_SIZE - 1) continue;
165-
// Set its grid to a wall cell
166-
this._set(seed.x, seed.y, 1);
167-
}
168-
// Now it's time to fill in the inaccessible pockets
169-
// Start at the top left
170-
let queue: number[][] = [[0, 0]];
171-
this._set(0, 0, 2);
172-
let checkedIndices = new Set([0]);
173-
// Now lets cycle through the whole map
174-
for (let i = 0; i < 3000 && queue.length > 0; i++) {
175-
let next = queue.shift();
176-
if (next == null) break;
177-
let [x, y] = next;
178-
// Get what the coordinates of what lies to the side of our cell
179-
for (let [nx, ny] of [
180-
[x - 1, y], // left
181-
[x + 1, y], // right
182-
[x, y - 1], // top
183-
[x, y + 1], // bottom
184-
]) {
185-
// If its a wall ignore it
186-
if (this._get(nx, ny) !== 0) continue;
187-
let i = ny * GRID_SIZE + nx;
188-
// Check if we've already checked this cell
189-
if (checkedIndices.has(i)) continue;
190-
// Add it to the checked cells if we haven't already
191-
checkedIndices.add(i);
192-
// Add it to the next cycle to check
193-
queue.push([nx, ny]);
194-
// Set its grid to an accessible cell
195-
this._set(nx, ny, 2);
196-
}
197-
}
198-
// Cycle through all areas of the map
199-
for (let x = 0; x < GRID_SIZE; x++) {
200-
for (let y = 0; y < GRID_SIZE; y++) {
201-
// If we're not a wall, ignore the cell and move on
202-
if (this._get(x, y) === 2) continue;
203-
// Define our properties
204-
let chunk = { x, y, width: 0, height: 1 };
205-
// Loop through adjacent cells and see how long we should be
206-
while (this._get(x + chunk.width, y) !== 2) {
207-
this._set(x + chunk.width, y, 2);
208-
chunk.width++;
209-
}
210-
// Now lets see if we need to be t h i c c
211-
outer: while (true) {
212-
// Check the row below to see if we can still make a box
213-
for (let i = 0; i < chunk.width; i++)
214-
// Stop if we can't
215-
if (this._get(x + i, y + chunk.height) === 2) break outer;
216-
// If we can, remove the line of cells from the map and increase the height of the block
217-
for (let i = 0; i < chunk.width; i++)
218-
this._set(x + i, y + chunk.height, 2);
219-
chunk.height++;
220-
}
221-
this.WALLS.push(chunk);
222-
}
223-
}
224-
// Create the walls!
225-
for (let {x, y, width, height} of this.WALLS)
226-
this._buildWallFromGridCoord(x, y, width, height);
22765
}
22866

22967
public isValidSpawnLocation(x: number, y: number): boolean {
23068
// Should never spawn inside walls
231-
for (let wall of this.WALLS) {
232-
const wallX = wall.x * CELL_SIZE - ARENA_SIZE / 2;
233-
const wallY = wall.y * CELL_SIZE - ARENA_SIZE / 2;
234-
const wallW = wall.width * CELL_SIZE;
235-
const wallH = wall.height * CELL_SIZE;
236-
if (
237-
x >= wallX &&
238-
x <= wallX + wallW &&
239-
y >= wallY &&
240-
y <= wallY + wallH
241-
) {
242-
return false;
243-
}
244-
}
245-
return true;
69+
return !this.mazeGenerator.isInWall(x, y);
24670
}
24771
}

0 commit comments

Comments
 (0)