mirror of
https://github.com/alexkurowski/solo-toolkit.git
synced 2026-07-22 10:10:32 +00:00
wip
This commit is contained in:
parent
2de49bd925
commit
320a2826bf
13 changed files with 188 additions and 473 deletions
|
|
@ -7,7 +7,6 @@ import {
|
|||
} from "./settings";
|
||||
import { SoloToolkitView, VIEW_TYPE } from "./view";
|
||||
import { soloToolkitExtension } from "./extension";
|
||||
import { MapgenModal } from "./modal";
|
||||
|
||||
export interface PluginApp extends App {
|
||||
isMobile?: boolean;
|
||||
|
|
@ -39,14 +38,6 @@ export default class SoloToolkitPlugin extends Plugin {
|
|||
callback: () => this.openView(),
|
||||
});
|
||||
|
||||
this.addCommand({
|
||||
id: "generate-tomb",
|
||||
name: "Generate a tomb",
|
||||
callback: () => {
|
||||
new MapgenModal(this.app).open();
|
||||
},
|
||||
});
|
||||
|
||||
this.addSettingTab(new SoloToolkitSettingTab(this.app, this));
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,36 +0,0 @@
|
|||
import { App, ButtonComponent, Modal } from "obsidian";
|
||||
import { drawMap } from "../utils/mapgen/draw";
|
||||
import { TombGenerator } from "../utils/mapgen/tomb";
|
||||
|
||||
export class MapgenModal extends Modal {
|
||||
map = new TombGenerator();
|
||||
|
||||
constructor(app: App) {
|
||||
super(app);
|
||||
}
|
||||
|
||||
onOpen() {
|
||||
const { contentEl } = this;
|
||||
|
||||
const canvas = contentEl.createEl("canvas");
|
||||
canvas.width = 1152;
|
||||
canvas.height = 1152;
|
||||
canvas.style.width = "528px";
|
||||
canvas.style.height = "528px";
|
||||
canvas.style.marginLeft = "auto";
|
||||
|
||||
new ButtonComponent(contentEl).setButtonText("Generate").onClick(() => {
|
||||
this.map = new TombGenerator();
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (ctx) drawMap(ctx, this.map);
|
||||
});
|
||||
|
||||
const ctx = canvas.getContext("2d");
|
||||
if (ctx) drawMap(ctx, this.map);
|
||||
}
|
||||
|
||||
onClose() {
|
||||
const { contentEl } = this;
|
||||
contentEl.empty();
|
||||
}
|
||||
}
|
||||
|
|
@ -4,4 +4,5 @@
|
|||
@import "./styles/dice";
|
||||
@import "./styles/word";
|
||||
@import "./styles/oracle";
|
||||
@import "./styles/map";
|
||||
@import "./styles/plugin";
|
||||
|
|
|
|||
|
|
@ -10,7 +10,7 @@
|
|||
|
||||
.deck-size {
|
||||
min-width: 50px;
|
||||
margin-left: var(--size-2-2);
|
||||
padding-left: var(--size-2-2);
|
||||
line-height: var(--input-height);
|
||||
text-align: right;
|
||||
}
|
||||
|
|
|
|||
31
src/styles/map.css
Normal file
31
src/styles/map.css
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
.srt-container {
|
||||
.map-buttons {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: var(--size-2-2);
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.map-results {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
gap: var(--size-4-4);
|
||||
width: 100%;
|
||||
overflow: hidden;
|
||||
|
||||
.map-result {
|
||||
}
|
||||
}
|
||||
|
||||
&.srt-desktop-layout {
|
||||
.map-results {
|
||||
flex-direction: column-reverse;
|
||||
margin-bottom: var(--size-4-6);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,4 +3,5 @@ export * from "./deck";
|
|||
export * from "./tarot";
|
||||
export * from "./word";
|
||||
export * from "./oracle";
|
||||
export * from "./map";
|
||||
export * from "./helpers";
|
||||
|
|
|
|||
15
src/utils/map.ts
Normal file
15
src/utils/map.ts
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
import { RoomGenerator } from "./mapgen/room";
|
||||
import { MapBlueprint } from "./mapgen/shared";
|
||||
// import { generateCave } from "./mapgen/cave";
|
||||
|
||||
export const generateMap = (type: string): MapBlueprint => {
|
||||
switch (type) {
|
||||
case "Room":
|
||||
return new RoomGenerator().map;
|
||||
case "Cave":
|
||||
// return generateCave();
|
||||
throw `Unknown map type: ${type}`;
|
||||
default:
|
||||
throw `Unknown map type: ${type}`;
|
||||
}
|
||||
};
|
||||
|
|
@ -1,13 +1,24 @@
|
|||
import { Map, MAP_WIDTH, MAP_HEIGHT } from "./shared";
|
||||
import { MapBlueprint } from "./shared";
|
||||
import { random } from "../dice";
|
||||
|
||||
const TILE_WIDTH = 48;
|
||||
const TILE_HEIGHT = 48;
|
||||
let mapWidth = 0;
|
||||
let mapHeight = 0;
|
||||
let tileWidth = 0;
|
||||
let tileHeight = 0;
|
||||
|
||||
const LINE_WALL = 3;
|
||||
const LINE_FLOOR = 0.25;
|
||||
const LINE_JAG = 2;
|
||||
|
||||
const drawTile = (opts: {
|
||||
const at = (map: MapBlueprint, x: number, y: number): number => {
|
||||
if (x < 0 || x >= mapWidth || y < 0 || y >= mapHeight) {
|
||||
return 0;
|
||||
} else {
|
||||
return map[x][y];
|
||||
}
|
||||
};
|
||||
|
||||
const drawFloor = (opts: {
|
||||
ctx: CanvasRenderingContext2D;
|
||||
x: number;
|
||||
y: number;
|
||||
|
|
@ -15,7 +26,7 @@ const drawTile = (opts: {
|
|||
const { ctx, x, y } = opts;
|
||||
|
||||
ctx.fillStyle = "#ffffffee";
|
||||
ctx.fillRect(x * TILE_WIDTH, y * TILE_HEIGHT, TILE_WIDTH, TILE_HEIGHT);
|
||||
ctx.fillRect(x * tileWidth, y * tileHeight, tileWidth, tileHeight);
|
||||
};
|
||||
|
||||
const drawHorizontalLine = (opts: {
|
||||
|
|
@ -29,15 +40,15 @@ const drawHorizontalLine = (opts: {
|
|||
|
||||
const ctx = opts.ctx;
|
||||
|
||||
const x1 = (opts.x1 < opts.x2 ? opts.x1 : opts.x2) * TILE_WIDTH;
|
||||
const x2 = (opts.x1 < opts.x2 ? opts.x2 : opts.x1) * TILE_WIDTH;
|
||||
const y = opts.y * TILE_HEIGHT;
|
||||
const x1 = (opts.x1 < opts.x2 ? opts.x1 : opts.x2) * tileWidth;
|
||||
const x2 = (opts.x1 < opts.x2 ? opts.x2 : opts.x1) * tileWidth;
|
||||
const y = opts.y * tileHeight;
|
||||
|
||||
ctx.lineWidth = opts.width;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x1, y);
|
||||
|
||||
const step = TILE_WIDTH;
|
||||
const step = tileWidth;
|
||||
const hstep = step / 2;
|
||||
|
||||
for (let i = x1; i < x2; i += step) {
|
||||
|
|
@ -63,15 +74,15 @@ const drawVerticalLine = (opts: {
|
|||
|
||||
const ctx = opts.ctx;
|
||||
|
||||
const x = opts.x * TILE_WIDTH;
|
||||
const y1 = (opts.y1 < opts.y2 ? opts.y1 : opts.y2) * TILE_HEIGHT;
|
||||
const y2 = (opts.y1 < opts.y2 ? opts.y2 : opts.y1) * TILE_HEIGHT;
|
||||
const x = opts.x * tileWidth;
|
||||
const y1 = (opts.y1 < opts.y2 ? opts.y1 : opts.y2) * tileHeight;
|
||||
const y2 = (opts.y1 < opts.y2 ? opts.y2 : opts.y1) * tileHeight;
|
||||
|
||||
ctx.lineWidth = opts.width;
|
||||
ctx.beginPath();
|
||||
ctx.moveTo(x, y1);
|
||||
|
||||
const step = TILE_HEIGHT;
|
||||
const step = tileHeight;
|
||||
const hstep = step / 2;
|
||||
|
||||
for (let i = y1; i < y2; i += step) {
|
||||
|
|
@ -86,13 +97,18 @@ const drawVerticalLine = (opts: {
|
|||
ctx.stroke();
|
||||
};
|
||||
|
||||
export const drawMap = (ctx: CanvasRenderingContext2D, map: Map) => {
|
||||
export const drawMap = (ctx: CanvasRenderingContext2D, map: MapBlueprint) => {
|
||||
mapWidth = map.length;
|
||||
mapHeight = map[0].length;
|
||||
tileWidth = Math.floor(ctx.canvas.width / mapWidth);
|
||||
tileHeight = Math.floor(ctx.canvas.height / mapHeight);
|
||||
|
||||
ctx.clearRect(0, 0, ctx.canvas.width, ctx.canvas.height);
|
||||
|
||||
for (let x = 0; x < MAP_WIDTH; x++) {
|
||||
for (let y = 0; y < MAP_HEIGHT; y++) {
|
||||
if (map.at(x, y) === 1) {
|
||||
drawTile({
|
||||
for (let x = 0; x < mapWidth; x++) {
|
||||
for (let y = 0; y < mapHeight; y++) {
|
||||
if (at(map, x, y) === 1) {
|
||||
drawFloor({
|
||||
ctx,
|
||||
x,
|
||||
y,
|
||||
|
|
@ -101,11 +117,11 @@ export const drawMap = (ctx: CanvasRenderingContext2D, map: Map) => {
|
|||
}
|
||||
}
|
||||
|
||||
for (let x = 0; x < MAP_WIDTH; x++) {
|
||||
for (let y = 0; y < MAP_HEIGHT; y++) {
|
||||
if (map.at(x, y) === 1) {
|
||||
for (let x = 0; x < mapWidth; x++) {
|
||||
for (let y = 0; y < mapHeight; y++) {
|
||||
if (at(map, x, y) === 1) {
|
||||
// Left wall
|
||||
if (map.at(x - 1, y) === 0) {
|
||||
if (at(map, x - 1, y) === 0) {
|
||||
drawVerticalLine({
|
||||
ctx,
|
||||
x: x,
|
||||
|
|
@ -114,8 +130,8 @@ export const drawMap = (ctx: CanvasRenderingContext2D, map: Map) => {
|
|||
width: LINE_WALL,
|
||||
});
|
||||
}
|
||||
// Right wall & floor
|
||||
if (map.at(x + 1, y) === 0) {
|
||||
// Right wall & floor line
|
||||
if (at(map, x + 1, y) === 0) {
|
||||
drawVerticalLine({
|
||||
ctx,
|
||||
x: x + 1,
|
||||
|
|
@ -133,7 +149,7 @@ export const drawMap = (ctx: CanvasRenderingContext2D, map: Map) => {
|
|||
});
|
||||
}
|
||||
// Up wall
|
||||
if (map.at(x, y - 1) === 0) {
|
||||
if (at(map, x, y - 1) === 0) {
|
||||
drawHorizontalLine({
|
||||
ctx,
|
||||
x1: x,
|
||||
|
|
@ -142,8 +158,8 @@ export const drawMap = (ctx: CanvasRenderingContext2D, map: Map) => {
|
|||
width: LINE_WALL,
|
||||
});
|
||||
}
|
||||
// Down wall & floor
|
||||
if (map.at(x, y + 1) === 0) {
|
||||
// Down wall & floor line
|
||||
if (at(map, x, y + 1) === 0) {
|
||||
drawHorizontalLine({
|
||||
ctx,
|
||||
x1: x,
|
||||
|
|
|
|||
65
src/utils/mapgen/room.ts
Normal file
65
src/utils/mapgen/room.ts
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
import { random } from "../dice";
|
||||
import { shuffle } from "../helpers";
|
||||
import { Cell, MapBlueprint, ROOM_HEIGHT, ROOM_WIDTH } from "./shared";
|
||||
|
||||
export class RoomGenerator {
|
||||
map: MapBlueprint = [];
|
||||
roomWidth = random(4, 8);
|
||||
roomHeight = random(4, 8);
|
||||
|
||||
constructor() {
|
||||
this.prepareMap();
|
||||
this.createRoom();
|
||||
}
|
||||
|
||||
private prepareMap() {
|
||||
this.map = [];
|
||||
for (let i = 0; i < ROOM_WIDTH; i++) {
|
||||
this.map.push([]);
|
||||
for (let j = 0; j < ROOM_HEIGHT; j++) {
|
||||
this.map[i].push(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private createRoom() {
|
||||
const x = Math.floor(ROOM_WIDTH / 2 - this.roomWidth / 2);
|
||||
const y = Math.floor(ROOM_HEIGHT / 2 - this.roomHeight / 2);
|
||||
|
||||
const doorPlaces: Cell[] = [];
|
||||
|
||||
for (let i = x; i < x + this.roomWidth; i++) {
|
||||
for (let j = y; j < y + this.roomHeight; j++) {
|
||||
this.map[i][j] = 1;
|
||||
// North
|
||||
if (j === y && i > x && i < x + this.roomWidth - 1) {
|
||||
doorPlaces.push({
|
||||
x: i,
|
||||
y: j - 1,
|
||||
});
|
||||
}
|
||||
// South
|
||||
if (
|
||||
j === y + this.roomHeight - 1 &&
|
||||
i > x &&
|
||||
i < x + this.roomWidth - 1
|
||||
) {
|
||||
doorPlaces.push({
|
||||
x: i,
|
||||
y: j + 1,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
shuffle(doorPlaces);
|
||||
|
||||
const doors = random(1, 5);
|
||||
for (let i = 0; i < doors; i++) {
|
||||
const cell = doorPlaces.pop();
|
||||
if (cell) {
|
||||
this.map[cell.x][cell.y] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,5 +1,5 @@
|
|||
export const MAP_WIDTH = 24;
|
||||
export const MAP_HEIGHT = 24;
|
||||
export const ROOM_WIDTH = 12;
|
||||
export const ROOM_HEIGHT = 12;
|
||||
|
||||
export type Cell = {
|
||||
x: number;
|
||||
|
|
@ -18,6 +18,8 @@ export type Connection = {
|
|||
idb: number;
|
||||
};
|
||||
|
||||
export type MapBlueprint = number[][];
|
||||
|
||||
export abstract class Map {
|
||||
abstract at(x: number, y: number): number;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,223 +0,0 @@
|
|||
import { random } from "../dice";
|
||||
import { Map, Room, Cell, MAP_HEIGHT, MAP_WIDTH } from "./shared";
|
||||
|
||||
const SPLITS = 4;
|
||||
|
||||
export class TombGenerator implements Map {
|
||||
roomCount: number = random(4, 8);
|
||||
map: number[][] = [];
|
||||
leafs: Room[] = [];
|
||||
rooms: Room[] = [];
|
||||
|
||||
constructor() {
|
||||
this.prepareMap();
|
||||
this.createLeafs();
|
||||
this.createRooms();
|
||||
// this.connectRooms();
|
||||
}
|
||||
|
||||
at(x: number, y: number): number {
|
||||
if (x < 0 || x >= MAP_WIDTH || y < 0 || y >= MAP_HEIGHT) {
|
||||
return 0;
|
||||
} else {
|
||||
return this.map[x][y];
|
||||
}
|
||||
}
|
||||
|
||||
private prepareMap() {
|
||||
this.map = [];
|
||||
for (let i = 0; i < MAP_WIDTH; i++) {
|
||||
this.map.push([]);
|
||||
for (let j = 0; j < MAP_HEIGHT; j++) {
|
||||
this.map[i].push(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private createLeafs() {
|
||||
this.leafs.push({ x: 0, y: 0, w: MAP_WIDTH, h: MAP_HEIGHT });
|
||||
|
||||
for (let i = 0; i < SPLITS; i++) {
|
||||
const newLeafs: Room[] = [];
|
||||
|
||||
for (const index in this.leafs) {
|
||||
const leaf = this.leafs[index];
|
||||
const splitHorizontally = random(1, 2) === 1;
|
||||
if (splitHorizontally && leaf.h >= 10) {
|
||||
const splitAt = Math.floor(leaf.h / 2) + random(-1, 1);
|
||||
newLeafs.push({
|
||||
x: leaf.x,
|
||||
y: leaf.y,
|
||||
w: leaf.w,
|
||||
h: splitAt,
|
||||
});
|
||||
newLeafs.push({
|
||||
x: leaf.x,
|
||||
y: leaf.y + splitAt,
|
||||
w: leaf.w,
|
||||
h: leaf.h - splitAt,
|
||||
});
|
||||
} else if (leaf.w >= 10) {
|
||||
const splitAt = Math.floor(leaf.w / 2) + random(-1, 1);
|
||||
newLeafs.push({
|
||||
x: leaf.x,
|
||||
y: leaf.y,
|
||||
w: splitAt,
|
||||
h: leaf.h,
|
||||
});
|
||||
newLeafs.push({
|
||||
x: leaf.x + splitAt,
|
||||
y: leaf.y,
|
||||
w: leaf.w - splitAt,
|
||||
h: leaf.h,
|
||||
});
|
||||
} else {
|
||||
newLeafs.push({
|
||||
x: leaf.x,
|
||||
y: leaf.y,
|
||||
w: leaf.w,
|
||||
h: leaf.h,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
this.leafs = newLeafs;
|
||||
|
||||
// const indexes: number[] = Object.keys(newLeafs)
|
||||
// .map((i) => Number(i))
|
||||
// .sort()
|
||||
// .reverse();
|
||||
// for (const index of indexes) {
|
||||
// const newLeaf = newLeafs[index];
|
||||
// if (newLeaf) {
|
||||
// this.leafs.splice(Number(index), 1, ...newLeaf);
|
||||
// }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
private createRooms() {
|
||||
for (const leaf of this.leafs) {
|
||||
for (let i = leaf.x; i < leaf.x + leaf.w - 1; i++) {
|
||||
for (let j = leaf.y; j < leaf.y + leaf.h - 1; j++) {
|
||||
this.map[i][j] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// let retryCount = 0;
|
||||
// const maxRetry = 100;
|
||||
|
||||
// for (let i = 0; i < this.roomCount; i++) {
|
||||
// const w = random(3, 5);
|
||||
// const h = random(3, 5);
|
||||
// const x = random(1, MAP_WIDTH - w - 2);
|
||||
// const y = random(1, MAP_HEIGHT - h - 2);
|
||||
|
||||
// if (this.isRoomOverlapsOther({ x, y, w, h })) {
|
||||
// if (retryCount > maxRetry) {
|
||||
// return;
|
||||
// } else {
|
||||
// retryCount++;
|
||||
// i--;
|
||||
// continue;
|
||||
// }
|
||||
// } else {
|
||||
// retryCount = 0;
|
||||
// }
|
||||
|
||||
// this.rooms.push({ x, y, w, h });
|
||||
|
||||
// for (let cx = 0; cx < w; cx++) {
|
||||
// for (let cy = 0; cy < h; cy++) {
|
||||
// this.map[x + cx][y + cy] = 1;
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
}
|
||||
|
||||
private connectRooms() {
|
||||
let cellToConnectA = { x: 0, y: 0 };
|
||||
let cellToConnectB = { x: 0, y: 0 };
|
||||
let distance = MAP_WIDTH * MAP_HEIGHT;
|
||||
|
||||
let connectionHash = "";
|
||||
const connectionHashes: string[] = []; // TODO: should be a tree or a linked list
|
||||
const getConnectionHash = (roomA: Room, roomB: Room) =>
|
||||
[this.rooms.indexOf(roomA), this.rooms.indexOf(roomB)].sort().join(".");
|
||||
|
||||
for (const roomA of this.rooms) {
|
||||
distance = MAP_WIDTH * MAP_HEIGHT;
|
||||
|
||||
for (const roomB of this.rooms) {
|
||||
if (roomA === roomB) continue;
|
||||
const hash = getConnectionHash(roomA, roomB);
|
||||
if (connectionHashes.includes(hash)) continue;
|
||||
|
||||
const cellA = {
|
||||
x: random(roomA.x + 1, roomA.x + roomA.w - 2),
|
||||
y: random(roomA.y + 1, roomA.y + roomA.h - 2),
|
||||
};
|
||||
const cellB = {
|
||||
x: random(roomB.x + 1, roomB.x + roomB.w - 2),
|
||||
y: random(roomB.y + 1, roomB.y + roomB.h - 2),
|
||||
};
|
||||
const roomDistance = Math.sqrt(
|
||||
Math.pow(cellB.x - cellA.x, 2) + Math.pow(cellB.y - cellA.y, 2)
|
||||
);
|
||||
|
||||
if (roomDistance < distance) {
|
||||
cellToConnectA = cellA;
|
||||
cellToConnectB = cellB;
|
||||
connectionHash = hash;
|
||||
distance = roomDistance;
|
||||
}
|
||||
}
|
||||
|
||||
connectionHashes.push(connectionHash);
|
||||
this.connectCells(cellToConnectA, cellToConnectB);
|
||||
}
|
||||
}
|
||||
|
||||
private connectCells(cellA: Cell, cellB: Cell) {
|
||||
let x = cellA.x;
|
||||
let y = cellA.y;
|
||||
let dx = cellB.x - x;
|
||||
let dy = cellB.y - y;
|
||||
let sx = 0;
|
||||
let sy = 0;
|
||||
|
||||
let goHorizontal = Math.abs(dx) > Math.abs(dy) ? true : false;
|
||||
|
||||
while (x !== cellB.x || y !== cellB.y) {
|
||||
dx = cellB.x - x;
|
||||
dy = cellB.y - y;
|
||||
sx = goHorizontal ? (dx > 0 ? 1 : -1) : 0;
|
||||
sy = goHorizontal ? 0 : dy > 0 ? 1 : -1;
|
||||
x += sx;
|
||||
y += sy;
|
||||
|
||||
this.map[x][y] = 1;
|
||||
|
||||
if (goHorizontal && x == cellB.x) {
|
||||
goHorizontal = false;
|
||||
} else if (!goHorizontal && y === cellB.y) {
|
||||
goHorizontal = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private isRoomOverlapsOther(roomA: Room): boolean {
|
||||
for (const roomB of this.rooms) {
|
||||
if (
|
||||
roomA.x - 1 < roomB.x + roomB.w &&
|
||||
roomA.x + roomA.w + 2 > roomB.x &&
|
||||
roomA.y - 1 < roomB.y + roomB.h &&
|
||||
roomA.y + roomA.h + 1 > roomB.y
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,149 +0,0 @@
|
|||
import { random } from "../dice";
|
||||
import { Room, Cell, MAP_HEIGHT, MAP_WIDTH } from "./shared";
|
||||
|
||||
export class TombGenerator {
|
||||
roomCount: number = random(4, 8);
|
||||
map: number[][] = [];
|
||||
rooms: Room[] = [];
|
||||
|
||||
constructor() {
|
||||
this.prepareMap();
|
||||
this.createRooms();
|
||||
this.connectRooms();
|
||||
}
|
||||
|
||||
at(x: number, y: number): number {
|
||||
if (x < 0 || x >= MAP_WIDTH || y < 0 || y >= MAP_HEIGHT) {
|
||||
return 0;
|
||||
} else {
|
||||
return this.map[x][y];
|
||||
}
|
||||
}
|
||||
|
||||
private prepareMap() {
|
||||
this.map = [];
|
||||
for (let i = 0; i < MAP_WIDTH; i++) {
|
||||
this.map.push([]);
|
||||
for (let j = 0; j < MAP_HEIGHT; j++) {
|
||||
this.map[i].push(0);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private createRooms() {
|
||||
let retryCount = 0;
|
||||
const maxRetry = 100;
|
||||
|
||||
for (let i = 0; i < this.roomCount; i++) {
|
||||
const w = random(3, 5);
|
||||
const h = random(3, 5);
|
||||
const x = random(1, MAP_WIDTH - w - 2);
|
||||
const y = random(1, MAP_HEIGHT - h - 2);
|
||||
|
||||
if (this.isRoomOverlapsOther({ x, y, w, h })) {
|
||||
if (retryCount > maxRetry) {
|
||||
return;
|
||||
} else {
|
||||
retryCount++;
|
||||
i--;
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
retryCount = 0;
|
||||
}
|
||||
|
||||
this.rooms.push({ x, y, w, h });
|
||||
|
||||
for (let cx = 0; cx < w; cx++) {
|
||||
for (let cy = 0; cy < h; cy++) {
|
||||
this.map[x + cx][y + cy] = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private connectRooms() {
|
||||
let cellToConnectA = { x: 0, y: 0 };
|
||||
let cellToConnectB = { x: 0, y: 0 };
|
||||
let distance = MAP_WIDTH * MAP_HEIGHT;
|
||||
|
||||
let connectionHash = "";
|
||||
const connectionHashes: string[] = []; // TODO: should be a tree or a linked list
|
||||
const getConnectionHash = (roomA: Room, roomB: Room) =>
|
||||
[this.rooms.indexOf(roomA), this.rooms.indexOf(roomB)].sort().join(".");
|
||||
|
||||
for (const roomA of this.rooms) {
|
||||
distance = MAP_WIDTH * MAP_HEIGHT;
|
||||
|
||||
for (const roomB of this.rooms) {
|
||||
if (roomA === roomB) continue;
|
||||
const hash = getConnectionHash(roomA, roomB);
|
||||
if (connectionHashes.includes(hash)) continue;
|
||||
|
||||
const cellA = {
|
||||
x: random(roomA.x + 1, roomA.x + roomA.w - 2),
|
||||
y: random(roomA.y + 1, roomA.y + roomA.h - 2),
|
||||
};
|
||||
const cellB = {
|
||||
x: random(roomB.x + 1, roomB.x + roomB.w - 2),
|
||||
y: random(roomB.y + 1, roomB.y + roomB.h - 2),
|
||||
};
|
||||
const roomDistance = Math.sqrt(
|
||||
Math.pow(cellB.x - cellA.x, 2) + Math.pow(cellB.y - cellA.y, 2)
|
||||
);
|
||||
|
||||
if (roomDistance < distance) {
|
||||
cellToConnectA = cellA;
|
||||
cellToConnectB = cellB;
|
||||
connectionHash = hash;
|
||||
distance = roomDistance;
|
||||
}
|
||||
}
|
||||
|
||||
connectionHashes.push(connectionHash);
|
||||
this.connectCells(cellToConnectA, cellToConnectB);
|
||||
}
|
||||
}
|
||||
|
||||
private connectCells(cellA: Cell, cellB: Cell) {
|
||||
let x = cellA.x;
|
||||
let y = cellA.y;
|
||||
let dx = cellB.x - x;
|
||||
let dy = cellB.y - y;
|
||||
let sx = 0;
|
||||
let sy = 0;
|
||||
|
||||
let goHorizontal = Math.abs(dx) > Math.abs(dy) ? true : false;
|
||||
|
||||
while (x !== cellB.x || y !== cellB.y) {
|
||||
dx = cellB.x - x;
|
||||
dy = cellB.y - y;
|
||||
sx = goHorizontal ? (dx > 0 ? 1 : -1) : 0;
|
||||
sy = goHorizontal ? 0 : dy > 0 ? 1 : -1;
|
||||
x += sx;
|
||||
y += sy;
|
||||
|
||||
this.map[x][y] = 1;
|
||||
|
||||
if (goHorizontal && x == cellB.x) {
|
||||
goHorizontal = false;
|
||||
} else if (!goHorizontal && y === cellB.y) {
|
||||
goHorizontal = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private isRoomOverlapsOther(roomA: Room): boolean {
|
||||
for (const roomB of this.rooms) {
|
||||
if (
|
||||
roomA.x - 1 < roomB.x + roomB.w &&
|
||||
roomA.x + roomA.w + 2 > roomB.x &&
|
||||
roomA.y - 1 < roomB.y + roomB.h &&
|
||||
roomA.y + roomA.h + 1 > roomB.y
|
||||
) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
|
@ -1,10 +1,14 @@
|
|||
import { ButtonComponent } from "obsidian";
|
||||
import { SoloToolkitView as View } from "./index";
|
||||
import { PluginApp } from "../main";
|
||||
import { clickToCopy } from "../utils";
|
||||
import { clickToCopy, generateMap } from "../utils";
|
||||
import { MapBlueprint } from "src/utils/mapgen/shared";
|
||||
import { drawMap } from "src/utils/mapgen/draw";
|
||||
|
||||
const MAX_REMEMBER_SIZE = 20;
|
||||
|
||||
export class MapView {
|
||||
view: View;
|
||||
maps: MapBlueprint[] = [];
|
||||
mapBtnsEl: HTMLElement;
|
||||
mapResultsEl: HTMLElement;
|
||||
|
||||
|
|
@ -19,9 +23,8 @@ export class MapView {
|
|||
|
||||
this.mapBtnsEl = this.view.tabViewEl.createDiv("map-buttons");
|
||||
this.mapBtnsEl.empty();
|
||||
this.createMapBtn("Tomb");
|
||||
this.createMapBtn("Room");
|
||||
this.createMapBtn("Cave");
|
||||
this.createMapBtn("Town");
|
||||
|
||||
if (!this.view.isMobile) {
|
||||
this.mapResultsEl = this.view.tabViewEl.createDiv("map-results");
|
||||
|
|
@ -35,17 +38,22 @@ export class MapView {
|
|||
this.mapResultsEl.empty();
|
||||
}
|
||||
|
||||
addResult(type: string, value: string, immediate = false) {
|
||||
addResult(map: MapBlueprint, immediate = false) {
|
||||
const elClass = ["map-result"];
|
||||
if (immediate) elClass.push("nofade");
|
||||
const el = this.mapResultsEl.createEl("a", { cls: elClass.join(" ") });
|
||||
el.onclick = clickToCopy(value);
|
||||
|
||||
const typeEl = el.createSpan("map-result-type");
|
||||
typeEl.setText(type);
|
||||
// TODO:
|
||||
// el.onclick = clickToCopy(value);
|
||||
|
||||
const valueEl = el.createSpan("map-result-value");
|
||||
valueEl.setText(value);
|
||||
const canvasEl = el.createEl("canvas");
|
||||
canvasEl.width = 1000;
|
||||
canvasEl.height = 1000;
|
||||
canvasEl.style.width = "300px";
|
||||
canvasEl.style.height = "300px";
|
||||
|
||||
const ctx = canvasEl.getContext("2d");
|
||||
if (ctx) drawMap(ctx, map);
|
||||
}
|
||||
|
||||
createMapBtn(type: string) {
|
||||
|
|
@ -53,25 +61,18 @@ export class MapView {
|
|||
.setButtonText(type)
|
||||
.setTooltip(`Generate a ${type.toLowerCase()}`)
|
||||
.onClick(() => {
|
||||
const app = this.view.app as PluginApp;
|
||||
if (app?.commands?.executeCommandById) {
|
||||
app.commands.executeCommandById(
|
||||
`solo-rpg-toolkit:generate-${type.toLowerCase()}`
|
||||
);
|
||||
}
|
||||
// const value = generateMap(type);
|
||||
// this.maps.push([type, value]);
|
||||
// this.addResult(type, value);
|
||||
const value = generateMap(type);
|
||||
this.maps.push(value);
|
||||
this.addResult(value);
|
||||
});
|
||||
}
|
||||
|
||||
repopulateResults() {
|
||||
// while (this.maps.length > MAX_REMEMBER_SIZE) {
|
||||
// this.maps.shift();
|
||||
// }
|
||||
// for (const map of this.maps) {
|
||||
// const [type, value] = map;
|
||||
// this.addResult(type, value, true);
|
||||
// }
|
||||
while (this.maps.length > MAX_REMEMBER_SIZE) {
|
||||
this.maps.shift();
|
||||
}
|
||||
for (const map of this.maps) {
|
||||
this.addResult(map, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue