Skip to content

Commit

Permalink
Add door destination replacing for puzzle rooms
Browse files Browse the repository at this point in the history
  • Loading branch information
krypciak committed Aug 24, 2024
1 parent b1548b4 commit 4ffbbc9
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 21 deletions.
8 changes: 4 additions & 4 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

9 changes: 8 additions & 1 deletion src/dungeon/builder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import { MapArrange, MapArrangeData } from '../map-arrange/map-arrange'
import { MapPicker, mapPickerConfigurable } from '../map-arrange/map-picker/configurable'
import { AreaInfo, constructMapsFromMapsArrange } from '../map-construct/map-construct'
import { initAllPuzzles } from '../maps/puzzle-data'
import { Dir } from '../util/geometry'
import { Item } from '../util/items'
import { setRandomSeed } from '../util/util'
import { DungeonPaths } from './paths'
Expand Down Expand Up @@ -57,13 +58,20 @@ export class DungeonBuilder {
}

const mapPicker: MapPicker = mapPickerConfigurable({
startDir: Dir.WEST,
root: {
type: 'DngPuzzleTunnel',
tunnelSize: tunnelSizeReg,
// size: roomSizeReg,
count: 5,
randomizeDirTryOrder,
// forcePuzzleMap: 'rhombus-dng/room-1',

followedBy: {
type: 'Simple',
size: roomSizeReg,
count: 1,
},
// followedBy: branch(
// 1,
// () => 1,
Expand Down Expand Up @@ -108,6 +116,5 @@ export class DungeonBuilder {
mapsConstruct[0].constructed.name,
ig.TeleportPosition.createFromJson({ marker: 'entrance_0', level: 0, baseZPos: 0, size: { x: 0, y: 0 } })
)
console.dir(mapsConstruct, { depth: null })
}
}
2 changes: 2 additions & 0 deletions src/map-arrange/map-arrange.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,12 @@ import { Vec2 } from '../util/vec2'
import { MapPicker } from './map-picker/configurable'

export interface TprArrange3d extends Vec2 {
level?: number
dir: Dir3d
destId: Id
destIndex?: number
noDrawConnection?: boolean
dontPlace?: boolean
}
export interface TprArrange extends TprArrange3d {
dir: Dir
Expand Down
4 changes: 4 additions & 0 deletions src/map-construct/map-construct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ import { MapTheme } from './theme'
export type TprDoorLikeType = 'Door' | 'TeleportGround'
export type TprType = TprDoorLikeType | 'TeleportField'

export function isEntityATpr(e: sc.MapModel.MapEntity): e is sc.MapModel.MapEntity<TprType> {
return e.type == 'Door' || e.type == 'TeleportGround' || e.type == 'TeleportField'
}

export interface MapConstruct extends MapArrange {
arrangeCopy: MapArrange
constructed: sc.MapModel.Map
Expand Down
104 changes: 93 additions & 11 deletions src/maps/dng-puzzle-tunnel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,11 +9,18 @@ import {
RoomPlaceOrder,
} from '../map-arrange/map-arrange'
import { MapPicker, registerMapPickerNodeConfig } from '../map-arrange/map-picker/configurable'
import { AreaInfo, MapConstruct, registerMapConstructor } from '../map-construct/map-construct'
import {
AreaInfo,
getTprName,
isEntityATpr,
MapConstruct,
registerMapConstructor,
TprType,
} from '../map-construct/map-construct'
import { Dir, DirU, Rect } from '../util/geometry'
import { assert, shuffleArray } from '../util/util'
import { Vec2 } from '../util/vec2'
import { getPuzzleList } from './puzzle-data'
import { getPuzzleList, PuzzleData } from './puzzle-data'
import { simpleMapConstructor } from './simple'

declare global {
Expand All @@ -26,6 +33,7 @@ declare global {
tunnelSize: Vec2
randomizeDirTryOrder?: boolean
followedBy?: MapPicker.ConfigNode
forcePuzzleMap?: string
}
}
}
Expand All @@ -42,6 +50,7 @@ export function das({
branchDone,
nodeId,
nodeProgress,
forcePuzzleMap,
}: {
mapPicker: MapPicker
exitTpr: TprArrange
Expand All @@ -52,6 +61,7 @@ export function das({
branchDone?: boolean
nodeId?: number
nodeProgress?: number
forcePuzzleMap?: string
}): NextQueueEntryGenerator<MapArrangeData> {
return (id, _, accesor) => {
const tpr: TprArrange = {
Expand Down Expand Up @@ -82,7 +92,9 @@ export function das({
}
if (!doesMapArrangeFit(accesor, map, id)) return null

const puzzles = shuffleArray(getPuzzleList(tpr.dir))
let puzzles = shuffleArray(getPuzzleList(tpr.dir))
if (forcePuzzleMap) puzzles = puzzles.filter(p => p.map == forcePuzzleMap)
// printMapArrangeQueue(accesor, 16, true, [], false, true)

return {
data: map,
Expand Down Expand Up @@ -131,7 +143,7 @@ export function das({
const room: RoomArrange = {
...rect,
walls: [true, true, true, true],
dontPlace: true
dontPlace: true,
}

map.rects.push(room)
Expand Down Expand Up @@ -173,12 +185,28 @@ registerMapConstructor(
mapsArranged: MapArrange[],
mapsConstructed: MapConstruct[]
) => {
const res = simpleMapConstructor(map, areaInfo, pathResolver, mapsArranged, mapsConstructed, [8, 1, 1, 1])
const puzzle = res.placeData!.puzzle!

const puzzle = map.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 exitTpr = removeUnwantedTprsFromMap(puzzleMap, puzzle.exitTpr)
removeCutscenesFromMap(puzzleMap)

if (exitTpr) {
const destId = map.id + 1
const prevExitTprIndex = map.restTprs.findIndex(tpr => tpr.destId == destId)
assert(prevExitTprIndex != -1)
const prevExitTpr = map.restTprs[prevExitTprIndex]
prevExitTpr.dontPlace = true

exitTpr.settings.name = getTprName(false, prevExitTprIndex)
exitTpr.settings.map = pathResolver(destId)
exitTpr.settings.marker = getTprName(true, prevExitTpr.destIndex ?? 0)
}

const res = simpleMapConstructor(map, areaInfo, pathResolver, mapsArranged, mapsConstructed, [8, 1, 1, 1])

const pastePos = Vec2.add(Vec2.divC(Vec2.copy(res.rects[0]), 16), {
x: puzzle.pasteOffset,
y: puzzle.pasteOffset,
Expand All @@ -192,13 +220,67 @@ registerMapConstructor(
{
disableEntities: false,
mergeLayers: false,
removeCutscenes: true,
// makePuzzlesUnique: true,
// uniqueId: puzzle.usel.id,
// uniqueSel: puzzle.usel.sel,
}
)

res.constructed = out
return res
}
)

function removeUnwantedTprsFromMap(
map: sc.MapModel.Map,
keep: PuzzleData.Tpr | undefined
): sc.MapModel.MapEntity<TprType> | undefined {
let foundToKeep: sc.MapModel.MapEntity<TprType> | undefined
map.entities = map.entities.filter(e => {
if (!isEntityATpr(e)) return true
if (!keep) return false
if (!(e.x == keep.x && e.y == keep.y && e.level == keep.level && e.type == keep.type)) return false
assert(!foundToKeep)
foundToKeep = e
return true
})
if (keep) assert(foundToKeep)
return foundToKeep
}

const cutsceneEventTypes = new Set<string>([
'ADD_MSG_PERSON',
'CLEAR_MSG',
'SHOW_MSG',
'SHOW_SIDE_MSG',
'SET_MSG_EXPRESSION',
'SET_TASK',
'SET_ENTITY_ON_TOP_OTHER',
'SET_CAMERA_TARGET',
'SET_CAMERA_POS',
'WAIT_UNTIL_ACTION_DONE',
'CLEAR_TASK',
])
const cutsceneActionTypes = new Set<string>(['ENTER_DOOR', 'MOVE_TO_POINT', 'NAVIGATE_TO_POINT', 'SET_FACE_TO_ENTITY'])
function removeCutscenesFromMap(map: sc.MapModel.Map) {
for (let enI = map.entities.length - 1; enI >= 0; enI--) {
const entity = map.entities[enI]

if (entity.type == 'NPC') {
map.entities.splice(enI, 1)
} else if (entity.type == 'EventTrigger') {
const events = entity.settings.event ?? []
for (let evI = events.length - 1; evI >= 0; evI--) {
const event = events[evI]

if (cutsceneEventTypes.has(event.type)) {
events.splice(evI)
} else if (event.type == 'DO_ACTION') {
for (let acI = event.action.length - 1; acI >= 0; acI--) {
const action = event.action[acI]
if (cutsceneActionTypes.has(action.type)) {
event.action.splice(acI, 1)
}
}
}
}
}
}
}
3 changes: 1 addition & 2 deletions src/maps/puzzle-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -65,11 +65,10 @@ function createPuzzleData(map: string, sel: PuzzleSelection, mapData: sc.MapMode
},
{ dist: 100e3, entity: undefined } as { dist: number; entity: sc.MapModel.MapEntity<TprType> | undefined }
)
if (dist < 200) break exitTprIf
if (dist > 200) break exitTprIf
assert(entity)

exitTpr = { ...entity }
Vec2.sub(exitTpr, selPos)
} else assert(false)

let completionCondition: { path: string; value: any } | undefined
Expand Down
3 changes: 2 additions & 1 deletion src/maps/simple.ts
Original file line number Diff line number Diff line change
Expand Up @@ -149,6 +149,7 @@ export const simpleMapConstructor = ((
const mic = baseMapConstruct(map, pathResolver(map.id), areaInfo.id, theme, extension)

function pushTprEntity(tpr: TprArrange3d, isEntrance: boolean, index: number) {
if (tpr.dontPlace) return
const name = getTprName(isEntrance, index)
const dir = DirU.flip(tpr.dir as Dir)
if (tpr.destId == -1) {
Expand All @@ -171,7 +172,7 @@ export const simpleMapConstructor = ((
type: 'Door',
x,
y,
level: 0,
level: tpr.level ?? 0,
settings: {
name,
map: pathResolver(tpr.destId),
Expand Down
2 changes: 0 additions & 2 deletions src/util/vec2.ts
Original file line number Diff line number Diff line change
Expand Up @@ -250,8 +250,6 @@ export namespace Vec2 {
Vec2.copy(gridCorner),
Vec2.mulC(Vec2.floor(Vec2.divC(Vec2.copy(gridCorner), interval)), interval)
)
assert(offset.x < interval)
assert(offset.y < interval)

const div = Vec2.divC(Vec2.sub(Vec2.copy(toSnap), offset), interval)

Expand Down

0 comments on commit 4ffbbc9

Please sign in to comment.