diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index e311b66..575e82c 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -28,7 +28,7 @@ importers: version: 1.1.22 cc-blitzkrieg: specifier: github:krypciak/cc-blitzkrieg - version: https://codeload.github.com/krypciak/cc-blitzkrieg/tar.gz/be28d62634ffe5b797046c49846fb821a8d35c6d + version: https://codeload.github.com/krypciak/cc-blitzkrieg/tar.gz/3a6cb9539d3d85bbd1be8c9eb456c58b4c6f5e7f cc-hotreload: specifier: github:krypciak/cc-hotreload version: https://codeload.github.com/krypciak/cc-hotreload/tar.gz/80118b9d099e24c6b57d52e76be47633babd18f1 @@ -423,8 +423,8 @@ packages: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} - cc-blitzkrieg@https://codeload.github.com/krypciak/cc-blitzkrieg/tar.gz/be28d62634ffe5b797046c49846fb821a8d35c6d: - resolution: {tarball: https://codeload.github.com/krypciak/cc-blitzkrieg/tar.gz/be28d62634ffe5b797046c49846fb821a8d35c6d} + cc-blitzkrieg@https://codeload.github.com/krypciak/cc-blitzkrieg/tar.gz/3a6cb9539d3d85bbd1be8c9eb456c58b4c6f5e7f: + resolution: {tarball: https://codeload.github.com/krypciak/cc-blitzkrieg/tar.gz/3a6cb9539d3d85bbd1be8c9eb456c58b4c6f5e7f} version: 0.5.9 cc-hotreload@https://codeload.github.com/krypciak/cc-hotreload/tar.gz/80118b9d099e24c6b57d52e76be47633babd18f1: @@ -1221,7 +1221,7 @@ snapshots: callsites@3.1.0: {} - cc-blitzkrieg@https://codeload.github.com/krypciak/cc-blitzkrieg/tar.gz/be28d62634ffe5b797046c49846fb821a8d35c6d: + cc-blitzkrieg@https://codeload.github.com/krypciak/cc-blitzkrieg/tar.gz/3a6cb9539d3d85bbd1be8c9eb456c58b4c6f5e7f: dependencies: '@types/jszip': 3.4.1 diff --git a/src/area/area-json-creator.ts b/src/area/area-json-creator.ts index aa2c7f3..111920a 100644 --- a/src/area/area-json-creator.ts +++ b/src/area/area-json-creator.ts @@ -40,6 +40,7 @@ declare global { drawEmptyRect?: Rect drawRect?: Rect & { x2: number; y2: number } areaRect?: Rect + wallsFull?: boolean })[] } interface Connection { @@ -114,7 +115,7 @@ export function createArea( min: Vec2.sub(Vec2.divC(Vec2.copy(boundsAbsolute), divider), offset), max: Vec2.sub(Vec2.divC(Rect.x2y2(boundsAbsolute), divider), offset), rects: map.rects.map(a => { - const copy = { ...a } + const copy: sc.AreaLoadable.SDCustom.Map['rects'][number] = { ...a } Rect.div(copy, divider) Vec2.sub(copy, offsetRelative) return copy diff --git a/src/area/custom-area-container.ts b/src/area/custom-area-container.ts index 884fa30..f08c209 100644 --- a/src/area/custom-area-container.ts +++ b/src/area/custom-area-container.ts @@ -242,6 +242,7 @@ sc.MapRoom.inject({ o.drawEmptyRect = Rect.copy(rect) rect.x2 = rect.x + rect.width - 1 rect.y2 = rect.y + rect.height - 1 + o.wallsFull ??= o.walls[0] && o.walls[1] && o.walls[2] && o.walls[3] if (o.walls[Dir.SOUTH]) { black.draw(rect.x, rect.y2, rect.width, 1) diff --git a/src/bun-run.ts b/src/bun-run.ts index 165bfed..8029587 100644 --- a/src/bun-run.ts +++ b/src/bun-run.ts @@ -1,12 +1,12 @@ import './setup-test' -import { DungeonBuilder } from './dungeon/builder' -import { initLibraries } from './library-providers' -;(async () => { - await initLibraries() - - const b = new DungeonBuilder() - b.build('das') -})() +// import { DungeonBuilder } from './dungeon/builder' +// import { initLibraries } from './library-providers' +// ;(async () => { +// await initLibraries() +// +// const b = new DungeonBuilder() +// b.build('das') +// })() // const b = new Test_DungeonBuilder() // b.samePlaceFail() diff --git a/src/dungeon/builder.ts b/src/dungeon/builder.ts index c049f16..2bdd642 100644 --- a/src/dungeon/builder.ts +++ b/src/dungeon/builder.ts @@ -58,11 +58,12 @@ export class DungeonBuilder { } const mapPicker: MapPicker = mapPickerConfigurable({ + startDir: Dir.WEST, root: { type: 'DngPuzzleTunnel', tunnelSize: tunnelSizeReg, // size: roomSizeReg, - count: 20, + count: 1, randomizeDirTryOrder, // followedBy: branch( diff --git a/src/map-arrange/map-arrange.ts b/src/map-arrange/map-arrange.ts index 9175daf..959a415 100644 --- a/src/map-arrange/map-arrange.ts +++ b/src/map-arrange/map-arrange.ts @@ -1,4 +1,5 @@ import { BuildQueueAccesor, Id, NextQueueEntryGenerator } from '../build-queue/build-queue' +import { PuzzleData } from '../maps/puzzle-data' import { Dir, Dir3d, Rect } from '../util/geometry' import { Vec2 } from '../util/vec2' import { MapPicker } from './map-picker/configurable' @@ -27,6 +28,10 @@ export interface MapArrange { nodeId?: number nodeProgress?: number + + placeData?: { + puzzle?: PuzzleData + } } export function copyMapArrange(map: MapArrangeData): MapArrange { @@ -44,6 +49,8 @@ export function copyMapArrange(map: MapArrangeData): MapArrange { nodeId: map.nodeId, nodeProgress: map.nodeProgress, + + placeData: map.placeData, } } @@ -55,8 +62,16 @@ export function offsetMapArrange(map: MapArrange, vec: Vec2) { export type MapArrangeData = Partial +export const RoomPlaceOrder = { + Room: 0, + Tunnel: 1, +} as const +export type RoomPlaceOrder = (typeof RoomPlaceOrder)[keyof typeof RoomPlaceOrder] + export interface RoomArrange extends Rect { walls: Record + placeOrder?: RoomPlaceOrder + dontPlace?: boolean } export function doesMapArrangeFit( diff --git a/src/map-construct/map-construct.ts b/src/map-construct/map-construct.ts index 1ed8b80..dc3f971 100644 --- a/src/map-construct/map-construct.ts +++ b/src/map-construct/map-construct.ts @@ -18,9 +18,7 @@ export interface MapConstruct extends MapArrange { rects: RoomConsturct[] } -export interface RoomConsturct extends RoomArrange { - wallsFull: boolean -} +export interface RoomConsturct extends RoomArrange {} export interface AreaInfo { id: string @@ -104,11 +102,7 @@ export function getTprName(isEntrance: boolean, index: number): string { return `${isEntrance ? 'entrance' : 'rest'}_${index}` } -function areWallsFull(walls: Record): boolean { - return walls[0] && walls[1] && walls[2] && walls[3] -} export function convertRoomsArrangeToRoomsConstruct(rooms: RoomArrange[]): RoomConsturct[] { - const newRooms = rooms.map(room => Object.assign(room, { wallsFull: areWallsFull(room.walls) }) as RoomConsturct) - newRooms.sort((a, b) => (b.wallsFull ? 1 : -1) - (a.wallsFull ? 1 : -1)) - return newRooms + rooms.sort((a, b) => (a.placeOrder ?? 0) - (b.placeOrder ?? 0)) + return rooms } diff --git a/src/map-construct/room.ts b/src/map-construct/room.ts index 5763571..ce5ac64 100644 --- a/src/map-construct/room.ts +++ b/src/map-construct/room.ts @@ -37,7 +37,6 @@ export function placeRoom(room: RoomArrange, map: MapInConstruction, tc: MapThem } } } - if (room.walls[Dir.NORTH]) { for (let x = rx; x < rx2; x++) { placeWall(map, tc, { x, y: ry }, Dir.NORTH) diff --git a/src/map-construct/theme.ts b/src/map-construct/theme.ts index f98d974..5f4e76f 100644 --- a/src/map-construct/theme.ts +++ b/src/map-construct/theme.ts @@ -12,9 +12,6 @@ export type MapThemeConfig = { wallDown: number[] wallRight: number[] wallLeft: number[] - addLight?: boolean - lightTile?: number - lightStep?: number } & ( | { addShadows?: false @@ -35,7 +32,13 @@ export type MapThemeConfig = { edgeShadowBottomRight: number[][] edgeShadowBottomLeft: number[][] } -) +) & ({ + addLight?: false +} | { + addLight: true + lightTile: number + lightStep: number +}) export class MapTheme { constructor(public config: MapThemeConfig) {} diff --git a/src/maps/dng-puzzle-tunnel.ts b/src/maps/dng-puzzle-tunnel.ts index 97809a5..d143e03 100644 --- a/src/maps/dng-puzzle-tunnel.ts +++ b/src/maps/dng-puzzle-tunnel.ts @@ -6,11 +6,12 @@ import { RoomArrange, doesMapArrangeFit, TprArrange3d, + RoomPlaceOrder, } from '../map-arrange/map-arrange' import { MapPicker, registerMapPickerNodeConfig } from '../map-arrange/map-picker/configurable' -import { registerMapConstructor } from '../map-construct/map-construct' +import { AreaInfo, MapConstruct, registerMapConstructor } from '../map-construct/map-construct' import { Dir, DirU, Rect } from '../util/geometry' -import { shuffleArray } from '../util/util' +import { assert, shuffleArray } from '../util/util' import { Vec2 } from '../util/vec2' import { getPuzzleList } from './puzzle-data' import { simpleMapConstructor } from './simple' @@ -76,7 +77,7 @@ export function das({ const rect = Rect.centered(tunnelSize, tpr) const walls: Record = [true, true, true, true] walls[exitTpr.dir] = false - tunnelEntrance = { ...rect, walls } + tunnelEntrance = { ...rect, walls, placeOrder: RoomPlaceOrder.Tunnel } map.rects.push(tunnelEntrance) } if (!doesMapArrangeFit(accesor, map, id)) return null @@ -90,27 +91,47 @@ export function das({ branchCount: puzzles.length, nextQueueEntryGenerator: (_, branch, accesor) => { - const map = { rects: [] as RoomArrange[], restTprs: [] as TprArrange3d[] } const puzzle = puzzles[branch] + const map = { + rects: [] as RoomArrange[], + restTprs: [] as TprArrange3d[], + placeData: { + puzzle, + }, + } satisfies MapArrangeData const bounds = Rect.boundsOfArr(puzzle.rects) - if (puzzle.sel.data.type == blitzkrieg.PuzzleRoomType.AddWalls) Rect.extend(bounds, 3 * 2 * 16) + Rect.extend(bounds, puzzle.pasteOffset * 2 * 16) const size = { x: bounds.width, y: bounds.height } - const offset = { x: 0, y: 0 } - if ((tunnelSize.x / 16) % 2 != (size.x / 16) % 2) offset.x += 16 - if ((tunnelSize.x / 16) % 2 != (size.y / 16) % 2) offset.y += 16 - Vec2.add(size, offset) + const rect = { + ...Vec2.snapToGrid( + tunnelEntrance, + 16, + Vec2.sub(Rect.middle(Rect.side(tunnelEntrance, exitTpr.dir)), puzzle.entrance.vec) + ), + width: size.x, + height: size.y, + } + + function assertRect(rect: Rect, rects1: Rect[]) { + const rects = rects1.map(Rect.copy) + const bounds = Rect.boundsOfArr(rects) - const rect = Rect.centered(size, { - ...Rect.middle(Rect.side(tunnelEntrance, exitTpr.dir)), - dir: tpr.dir, - }) - // const rect = Rect.centered(size, tpr) + const rect1 = Rect.copy(rect) + Vec2.sub(rect1, bounds) + assert(rect1.x % 16 == 0) + assert(rect1.y % 16 == 0) + assert(rect1.width % 16 == 0) + assert(rect1.height % 16 == 0) + } + assertRect(tunnelEntrance, [tunnelEntrance, rect]) + assertRect(rect, [tunnelEntrance, rect]) const room: RoomArrange = { ...rect, walls: [true, true, true, true], + dontPlace: true } map.rects.push(room) @@ -143,4 +164,41 @@ export function das({ } } -registerMapConstructor('DngPuzzleTunnel', simpleMapConstructor) +registerMapConstructor( + 'DngPuzzleTunnel', + ( + map: MapArrange, + areaInfo: AreaInfo, + pathResolver: (id: Id) => string, + mapsArranged: MapArrange[], + mapsConstructed: MapConstruct[] + ) => { + const res = simpleMapConstructor(map, areaInfo, pathResolver, mapsArranged, mapsConstructed, [8, 1, 1, 1]) + const puzzle = res.placeData!.puzzle! + + const puzzleMapRaw: string = blitzkrieg.mapUtil.cachedMaps[puzzle.map] + assert(puzzleMapRaw) + const puzzleMap: sc.MapModel.Map = JSON.parse(blitzkrieg.mapUtil.cachedMaps[puzzle.map]) + const pastePos = Vec2.add(Vec2.divC(Vec2.copy(res.rects[0]), 16), { + x: puzzle.pasteOffset, + y: puzzle.pasteOffset, + }) + const out: sc.MapModel.Map = blitzkrieg.mapUtil.copySelMapAreaTo( + res.constructed, + puzzleMap, + puzzle.sel, + pastePos, + [], + { + disableEntities: false, + mergeLayers: false, + removeCutscenes: true, + // makePuzzlesUnique: true, + // uniqueId: puzzle.usel.id, + // uniqueSel: puzzle.usel.sel, + } + ) + res.constructed = out + return res + } +) diff --git a/src/maps/puzzle-data.ts b/src/maps/puzzle-data.ts index 75a61c0..e06e2e2 100644 --- a/src/maps/puzzle-data.ts +++ b/src/maps/puzzle-data.ts @@ -19,6 +19,7 @@ export interface PuzzleData { exit: ReturnType exitTpr?: PuzzleData.Tpr completionCondition?: { path: string; value: any } + pasteOffset: number } let allPuzzles!: PuzzleData[] @@ -84,6 +85,9 @@ function createPuzzleData(map: string, sel: PuzzleSelection, mapData: sc.MapMode const entrance = Rect.closestSideArr(rects, Vec2.sub(Vec2.copy(sel.data.startPos), selPos)) const exit = Rect.closestSideArr(rects, Vec2.sub(Vec2.copy(sel.data.endPos), selPos)) + + const pasteOffset = sel.data.type == blitzkrieg.PuzzleRoomType.AddWalls ? 3 : 0 + const res: PuzzleData = { sel, rects, @@ -92,6 +96,7 @@ function createPuzzleData(map: string, sel: PuzzleSelection, mapData: sc.MapMode exit, exitTpr, completionCondition, + pasteOffset, } if (res.entrance.dir == res.exit.dir) return diff --git a/src/maps/simple-branch.ts b/src/maps/simple-branch.ts index 04cafef..f898175 100644 --- a/src/maps/simple-branch.ts +++ b/src/maps/simple-branch.ts @@ -6,6 +6,7 @@ import { RoomArrange, doesMapArrangeFit, TprArrange3d, + RoomPlaceOrder, } from '../map-arrange/map-arrange' import { MapPicker, registerMapPickerNodeConfig } from '../map-arrange/map-picker/configurable' import { registerMapConstructor } from '../map-construct/map-construct' @@ -68,7 +69,7 @@ export function simpleMapBranchTunnelArrange({ const rect = Rect.centered(tunnelSize, tpr) const walls: Record = [true, true, true, true] walls[exitTpr.dir] = false - tunnelEntrance = { ...rect, walls } + tunnelEntrance = { ...rect, walls, placeOrder: RoomPlaceOrder.Tunnel } map.rects.push(tunnelEntrance) } let room: RoomArrange @@ -127,7 +128,7 @@ export function simpleMapBranchTunnelArrange({ }) const walls: Record = [true, true, true, true] walls[DirU.flip(dir)] = false - tunnelExit = { ...rect, walls } + tunnelExit = { ...rect, walls, placeOrder: RoomPlaceOrder.Tunnel } map.rects.push(tunnelExit) } if (!doesMapArrangeFit(accesor, map, id)) return null diff --git a/src/maps/simple-tunnel.ts b/src/maps/simple-tunnel.ts index d1eb148..3553c89 100644 --- a/src/maps/simple-tunnel.ts +++ b/src/maps/simple-tunnel.ts @@ -6,6 +6,7 @@ import { RoomArrange, doesMapArrangeFit, TprArrange3d, + RoomPlaceOrder, } from '../map-arrange/map-arrange' import { MapPicker, registerMapPickerNodeConfig } from '../map-arrange/map-picker/configurable' import { registerMapConstructor } from '../map-construct/map-construct' @@ -81,7 +82,7 @@ export function simpleMapTunnelArrange({ const rect = Rect.centered(tunnelSize, tpr) const walls: Record = [true, true, true, true] walls[exitTpr.dir] = false - tunnelEntrance = { ...rect, walls } + tunnelEntrance = { ...rect, walls, placeOrder: RoomPlaceOrder.Tunnel } map.rects.push(tunnelEntrance) } let room: RoomArrange @@ -122,7 +123,7 @@ export function simpleMapTunnelArrange({ }) const walls: Record = [true, true, true, true] walls[DirU.flip(dir)] = false - tunnelExit = { ...rect, walls } + tunnelExit = { ...rect, walls, placeOrder: RoomPlaceOrder.Tunnel } map.rects.push(tunnelExit) } if (!doesMapArrangeFit(accesor, map, id)) return null diff --git a/src/maps/simple.ts b/src/maps/simple.ts index 71a71df..1b1c803 100644 --- a/src/maps/simple.ts +++ b/src/maps/simple.ts @@ -136,16 +136,17 @@ export function simpleMapArrange({ } } -export const simpleMapConstructor: MapConstructFunc = ( +export const simpleMapConstructor = (( map, areaInfo, pathResolver, _mapsArranged, - _mapsConstructed + _mapsConstructed, + extension: Record = [8, 1, 1, 1] ) => { const theme = MapTheme.default const arrangeCopy = copyMapArrange(map) - const mic = baseMapConstruct(map, pathResolver(map.id), areaInfo.id, theme, [8, 1, 1, 1]) + const mic = baseMapConstruct(map, pathResolver(map.id), areaInfo.id, theme, extension) function pushTprEntity(tpr: TprArrange3d, isEntrance: boolean, index: number) { const name = getTprName(isEntrance, index) @@ -185,6 +186,7 @@ export const simpleMapConstructor: MapConstructFunc = ( const rects = convertRoomsArrangeToRoomsConstruct(map.rects) for (const room of rects) { + if (room.dontPlace) continue placeRoom(room, mic, theme.config, true) } @@ -197,6 +199,6 @@ export const simpleMapConstructor: MapConstructFunc = ( arrangeCopy, title: `map ${constructed.name}`, } -} +}) satisfies MapConstructFunc registerMapConstructor('Simple', simpleMapConstructor) diff --git a/src/util/geometry.spec.ts b/src/util/geometry.spec.ts index 3bd73d1..badfdaa 100644 --- a/src/util/geometry.spec.ts +++ b/src/util/geometry.spec.ts @@ -25,10 +25,10 @@ export class Test_Geometry { ) } @Test() - @TestCase('side north', { x: 1, y: 2, width: 2, height: 4 }, Dir.NORTH, { x: 1, y: 2, width: 2, height: 0 }) - @TestCase('side east', { x: 1, y: 2, width: 2, height: 4 }, Dir.EAST, { x: 3, y: 2, width: 0, height: 4 }) - @TestCase('side south', { x: 1, y: 2, width: 2, height: 4 }, Dir.SOUTH, { x: 1, y: 6, width: 2, height: 0 }) - @TestCase('side west', { x: 1, y: 2, width: 2, height: 4 }, Dir.WEST, { x: 1, y: 2, width: 0, height: 4 }) + @TestCase('side N', { x: 1, y: 2, width: 2, height: 4 }, Dir.NORTH, { x: 1, y: 2, width: 2, height: 0 }) + @TestCase('side E', { x: 1, y: 2, width: 2, height: 4 }, Dir.EAST, { x: 3, y: 2, width: 0, height: 4 }) + @TestCase('side S', { x: 1, y: 2, width: 2, height: 4 }, Dir.SOUTH, { x: 1, y: 6, width: 2, height: 0 }) + @TestCase('side W', { x: 1, y: 2, width: 2, height: 4 }, Dir.WEST, { x: 1, y: 2, width: 0, height: 4 }) side(rect: Rect, dir: Dir, expected: Rect) { const res = Rect.side(rect, dir) expect.toBeTrue( @@ -38,10 +38,10 @@ export class Test_Geometry { } @Test() - @TestCase('corner north east', { x: 1, y: 2, width: 2, height: 4 }, Dir.EAST, Dir.NORTH, { x: 3, y: 2 }) - @TestCase('corner north west', { x: 1, y: 2, width: 2, height: 4 }, Dir.WEST, Dir.NORTH, { x: 1, y: 2 }) - @TestCase('corner south east', { x: 1, y: 2, width: 2, height: 4 }, Dir.EAST, Dir.SOUTH, { x: 3, y: 6 }) - @TestCase('corner south west', { x: 1, y: 2, width: 2, height: 4 }, Dir.WEST, Dir.SOUTH, { x: 1, y: 6 }) + @TestCase('corner NE', { x: 1, y: 2, width: 2, height: 4 }, Dir.EAST, Dir.NORTH, { x: 3, y: 2 }) + @TestCase('corner NW', { x: 1, y: 2, width: 2, height: 4 }, Dir.WEST, Dir.NORTH, { x: 1, y: 2 }) + @TestCase('corner SE', { x: 1, y: 2, width: 2, height: 4 }, Dir.EAST, Dir.SOUTH, { x: 3, y: 6 }) + @TestCase('corner SW', { x: 1, y: 2, width: 2, height: 4 }, Dir.WEST, Dir.SOUTH, { x: 1, y: 6 }) corner(rect: Rect, h: typeof Dir.EAST | typeof Dir.WEST, v: typeof Dir.NORTH | typeof Dir.SOUTH, expected: Vec2) { const res = Rect.corner(rect, h, v) expect.toBeTrue( @@ -60,4 +60,17 @@ export class Test_Geometry { const res = Rect.doOverlap(r1, r2) expect.toBeEqual(res, expected) } + + @Test() + @TestCase('far away', { x: 0, y: 0, width: 2, height: 2 }, { x: 9, y: 9 }, false) + @TestCase('away', { x: 0, y: 0, width: 2, height: 2 }, { x: 1, y: 9 }, false) + @TestCase('inside', { x: 0, y: 0, width: 10, height: 10 }, { x: 5, y: 5 }, true) + @TestCase('inside corner NW', { x: 0, y: 0, width: 2, height: 2 }, { x: 0, y: 0 }, true) + @TestCase('inside corner NE', { x: 0, y: 0, width: 2, height: 2 }, { x: 2, y: 0 }, true) + @TestCase('inside corner SW', { x: 0, y: 0, width: 2, height: 2 }, { x: 0, y: 2 }, true) + @TestCase('inside corner SE', { x: 0, y: 0, width: 2, height: 2 }, { x: 2, y: 2 }, true) + isVecIn(rect: Rect, vec: Vec2, expected: boolean) { + const res = Rect.isVecIn(rect, vec) + expect.toBeEqual(res, expected) + } } diff --git a/src/util/geometry.ts b/src/util/geometry.ts index 749d933..8da79ef 100644 --- a/src/util/geometry.ts +++ b/src/util/geometry.ts @@ -201,6 +201,29 @@ export namespace Rect { if (dir == Dir.SOUTH) return { x: vec.x, y: Rect.y2(rect) } return { x: vec.x, y: vec.y } } + export function closestCorner( + rect: Rect, + vec: Vec2 + ): { distance: number; h: typeof Dir.EAST | typeof Dir.WEST; v: typeof Dir.NORTH | typeof Dir.SOUTH; vec: Vec2 } { + return ( + [ + [Dir.EAST, Dir.NORTH], + [Dir.WEST, Dir.NORTH], + [Dir.EAST, Dir.SOUTH], + [Dir.WEST, Dir.SOUTH], + ] as const + ).reduce( + (acc, [h, v]) => { + const corner = Rect.corner(rect, h, v) + const distance = Vec2.distance(vec, corner) + if (distance < acc.distance) { + return { distance, h, v, vec: corner } + } + return acc + }, + { distance: 100e3 } as ReturnType + ) + } export function closestSide(rect: Rect, vec: Vec2): { distance: number; dir: Dir; vec: Vec2 } { let smallest: { distance: number; dir: Dir; vec: Vec2 } = { distance: 10000, @@ -208,7 +231,7 @@ export namespace Rect { vec: { x: 0, y: 0 }, } for (let dir = Dir.NORTH; dir < 4; dir++) { - const v: Vec2 = Rect.side(rect, dir) + const v: Vec2 = Vec2.copy(Rect.side(rect, dir)) if (DirU.isVertical(dir)) { v.x = vec.x } else { @@ -254,7 +277,7 @@ export namespace Rect { return smallest } export function isVecIn(rect: Rect, vec: Vec2): boolean { - return vec.x >= rect.x && vec.x < rect.x + rect.width && vec.y >= rect.y && vec.y < rect.y + rect.height + return vec.x >= rect.x && vec.x <= rect.x + rect.width && vec.y >= rect.y && vec.y <= rect.y + rect.height } export function isVecInArr(rects: Rect[], vec: Vec2): boolean { for (const rect of rects) {