shine light from the sky
This commit is contained in:
parent
5db90bb2f3
commit
811b6e5b78
7 changed files with 182 additions and 117 deletions
20
src/App.vue
20
src/App.vue
|
@ -31,6 +31,7 @@ const floorY = computed(() => Math.floor(y.value))
|
|||
const tx = computed(() => (x.value - floorX.value) * -BLOCK_SIZE)
|
||||
const ty = computed(() => (y.value - floorY.value) * -BLOCK_SIZE)
|
||||
const rows = computed(() => level.grid(floorX.value, floorY.value))
|
||||
const lightBarrier = computed(() => level.sunLight(floorX.value))
|
||||
|
||||
const arriving = ref(true)
|
||||
const walking = ref(false)
|
||||
|
@ -125,10 +126,24 @@ const move = (thisTick: number): void => {
|
|||
lastTick = thisTick
|
||||
}
|
||||
|
||||
function calcBrightness(level: number, row: number) {
|
||||
const barrier = lightBarrier.value[row]
|
||||
let delta = barrier - level - (floorY.value - 3)
|
||||
|
||||
if (delta > 3) delta = 3
|
||||
else if (delta < 0) delta = 0
|
||||
|
||||
return `sun-${delta}`
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
lastTick = performance.now()
|
||||
move(lastTick)
|
||||
})
|
||||
|
||||
function log(...args: any[]) {
|
||||
console.log(...args)
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
|
@ -136,7 +151,10 @@ onMounted(() => {
|
|||
|
||||
<div id="blocks" :style="{transform: `translate(${tx}px, ${ty}px)`}">
|
||||
<template v-for="(row, y) in rows">
|
||||
<div v-for="(block, x) in row" class="block" :class="[block.type]" />
|
||||
<div v-for="(block, x) in row"
|
||||
:class="['block', block.type, calcBrightness(y, x)]"
|
||||
@click="log('block', x, y, block.type)"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
|
||||
|
|
|
@ -1,39 +1,3 @@
|
|||
.block.grass { background-image: url(/Tiles/dirt_grass.png); }
|
||||
|
||||
.block.treeTopLeft { background-image: url(/Tiles/leaves_transparent.png); }
|
||||
.block.treeTopMiddle { background-image: url(/Tiles/leaves_transparent.png); }
|
||||
.block.treeTopRight { background-image: url(/Tiles/leaves_transparent.png); }
|
||||
|
||||
.block.treeCrownLeft { background-image: url(/Tiles/leaves_transparent.png); }
|
||||
.block.treeCrownMiddle { background-image: url(/Tiles/leaves_transparent.png); }
|
||||
.block.treeCrownRight { background-image: url(/Tiles/leaves_transparent.png); }
|
||||
|
||||
.block.treeTrunkLeft { background-image: url(/Tiles/leaves_transparent.png); }
|
||||
.block.treeTrunkMiddle { background-image: url(/Tiles/trunk_mid.png); }
|
||||
.block.treeTrunkRight { background-image: url(/Tiles/leaves_transparent.png); }
|
||||
|
||||
.block.treeRootLeft { background-image: url(/Tiles/tree_root_left.png); }
|
||||
.block.treeRootMiddle { background-image: url(/Tiles/trunk_bottom.png); }
|
||||
.block.treeRootRight { background-image: url(/Tiles/tree_root_right.png); }
|
||||
|
||||
.block.soil { background-image: url(/Tiles/dirt.png); }
|
||||
.block.soilGravel { background-image: url(/Tiles/gravel_dirt.png); }
|
||||
.block.stoneGravel { background-image: url(/Tiles/gravel_stone.png); }
|
||||
.block.stone { background-image: url(/Tiles/stone.png); }
|
||||
.block.bedrock { background-image: url(/Tiles/greystone.png); }
|
||||
.block.cave { background-color: #000; }
|
||||
#field .block:hover { outline: 1px solid white; z-index: 10; }
|
||||
|
||||
.morning0 .block, .morning0 #player { filter: saturate(50%) brightness(0.6) hue-rotate(-10deg); }
|
||||
.morning1 .block, .morning1 #player { filter: saturate(100%) brightness(0.8) hue-rotate(-20deg); }
|
||||
.morning2 .block, .morning2 #player { filter: saturate(200%) hue-rotate(-30deg); }
|
||||
|
||||
.evening0 .block, .evening0 #player { filter: brightness(0.8) hue-rotate(-10deg); }
|
||||
.evening1 .block, .evening1 #player { filter: brightness(0.6) hue-rotate(-20deg); }
|
||||
.evening2 .block, .evening2 #player { filter: brightness(0.4) hue-rotate(-10deg) saturate(50%); }
|
||||
|
||||
.night .block, .night #player { filter: brightness(0.3) saturate(30%); }
|
||||
|
||||
.block {
|
||||
flex: 0 0 auto;
|
||||
width: var(--block-size);
|
||||
|
@ -43,6 +7,60 @@
|
|||
background-position: center;
|
||||
background-repeat: no-repeat;
|
||||
}
|
||||
|
||||
.block.grass { background-image: url(/Tiles/dirt_grass.png); }
|
||||
|
||||
.block.treeCrown, .block.treeLeaves { background-image: url(/Tiles/leaves_transparent.png); }
|
||||
.block.treeTrunk { background-image: url(/Tiles/trunk_mid.png); }
|
||||
.block.treeRoot { background-image: url(/Tiles/trunk_bottom.png); }
|
||||
|
||||
.block.soil { background-image: url(/Tiles/dirt.png); }
|
||||
.block.soilGravel { background-image: url(/Tiles/gravel_dirt.png); }
|
||||
.block.stoneGravel { background-image: url(/Tiles/gravel_stone.png); }
|
||||
.block.stone { background-image: url(/Tiles/stone.png); }
|
||||
.block.bedrock { background-image: url(/Tiles/greystone.png); }
|
||||
.block.cave { background-color: #000; }
|
||||
#field .block:hover { outline: 1px solid white; z-index: 10; }
|
||||
|
||||
.block.sun-3 { filter: brightness(1.0); }
|
||||
.block.sun-2 { filter: brightness(0.4); }
|
||||
.block.sun-1 { filter: brightness(0.2); }
|
||||
.block.sun-0 { filter: brightness(0.0); }
|
||||
|
||||
.morning0 .block, .morning0 #player {filter: saturate(50%) brightness(0.6) hue-rotate(-10deg); }
|
||||
.morning1 .block, .morning1 #player { filter: saturate(100%) brightness(0.8) hue-rotate(-20deg); }
|
||||
.morning2 .block, .morning2 #player { filter: saturate(200%) hue-rotate(-30deg); }
|
||||
|
||||
.morning0 .block.sun-2 { filter: brightness(0); }
|
||||
.morning1 .block.sun-2 { filter: saturate(100%) brightness(0.2) hue-rotate(-20deg); }
|
||||
.morning2 .block.sun-2 { filter: saturate(200%) brightness(0.4) hue-rotate(-30deg); }
|
||||
|
||||
.morning0 .block.sun-1,
|
||||
.morning1 .block.sun-1 { filter: brightness(0); }
|
||||
.morning2 .block.sun-1 { filter: saturate(200%) brightness(0.2) hue-rotate(-30deg); }
|
||||
|
||||
.evening0 .block, .evening0 #player { filter: brightness(0.8) hue-rotate(-10deg); }
|
||||
.evening1 .block, .evening1 #player { filter: brightness(0.6) hue-rotate(-20deg); }
|
||||
.evening2 .block, .evening2 #player { filter: brightness(0.4) hue-rotate(-10deg) saturate(50%); }
|
||||
|
||||
.evening0 .block.sun-2 { filter: brightness(0.3) hue-rotate(-10deg); }
|
||||
.evening1 .block.sun-2 { filter: brightness(0.2) hue-rotate(-20deg); }
|
||||
.evening2 .block.sun-2 { filter: brightness(0); }
|
||||
|
||||
.evening0 .block.sun-1 { filter: brightness(0.2) hue-rotate(-10deg); }
|
||||
.evening1 .block.sun-1, .evening2 .block.sun-1 { filter: brightness(0); }
|
||||
|
||||
.night .block, .night #player { filter: brightness(0.3) saturate(30%); }
|
||||
|
||||
.block.sun-0,
|
||||
.morning0 .block.sun-0,
|
||||
.morning1 .block.sun-0,
|
||||
.morning2 .block.sun-0,
|
||||
.evening0 .block.sun-0,
|
||||
.evening1 .block.sun-0,
|
||||
.evening2 .block.sun-0,
|
||||
.night .block.sun-0 { filter: brightness(0); }
|
||||
|
||||
#blocks {
|
||||
position: absolute;
|
||||
top: calc(var(--block-size) * (var(--spare-blocks) / -2));
|
||||
|
|
|
@ -1,43 +1,50 @@
|
|||
import type { NoiseFunction2D } from 'simplex-noise'
|
||||
import {blockTypes as T, level as L, probability as P, type Block} from './def'
|
||||
|
||||
const TREE_ROOT = [T.treeRootLeft, T.treeRootMiddle, T.treeRootRight]
|
||||
|
||||
function trees(r: number, i: number, row: Block[], previousRow: Block[]) {
|
||||
const max = row.length - 1
|
||||
const h = i - 1
|
||||
const j = i + 1
|
||||
const current = row[i]
|
||||
const above = previousRow[i]
|
||||
const aboveLeft = previousRow[h]
|
||||
const aboveRight = previousRow[j]
|
||||
|
||||
if (row[i] === T.treeTopMiddle) {
|
||||
if (i) {
|
||||
if (row[h] === T.treeTopRight) row[h] = T.treeTopLeftMixed
|
||||
else row[h] = T.treeTopLeft
|
||||
}
|
||||
if (i < max) row[j] = T.treeTopRight
|
||||
if (current === T.treeCrown) {
|
||||
if (i > 0) row[h] = T.treeLeaves
|
||||
if (i < max) row[j] = T.treeLeaves
|
||||
|
||||
} else if (previousRow[i] === T.treeTopMiddle) {
|
||||
row[i] = T.treeCrownMiddle
|
||||
if (i) {
|
||||
if (row[h] === T.treeCrownRight) row[h] = T.treeCrownLeftMixed
|
||||
else row[h] = T.treeCrownLeft
|
||||
}
|
||||
if (i < max) row[j] = T.treeCrownRight
|
||||
} else if (previousRow[i] === T.treeCrownMiddle) {
|
||||
row[i] = T.treeTrunkMiddle
|
||||
if (i) {
|
||||
if (row[h] === T.treeTrunkRight) row[h] = T.treeTrunkLeftMixed
|
||||
else row[h] = T.treeTrunkLeft
|
||||
}
|
||||
if (i < max) row[j] = T.treeTrunkRight
|
||||
} else if (previousRow[i] === T.treeTrunkMiddle) {
|
||||
row[i] = T.treeRootMiddle
|
||||
if (i) {
|
||||
if (row[h] === T.treeRootRight) row[h] = T.treeRootLeftMixed
|
||||
else row[h] = T.treeRootLeft
|
||||
}
|
||||
if (i < max) row[j] = T.treeRootRight
|
||||
} else if (above === T.treeCrown) {
|
||||
row[i] = T.treeLeaves
|
||||
if (i > 0) row[h] = T.treeLeaves
|
||||
if (i < max) row[j] = T.treeLeaves
|
||||
|
||||
} else if (above === T.treeLeaves && aboveLeft === T.treeLeaves && aboveRight === T.treeLeaves) {
|
||||
row[i] = T.treeTrunk
|
||||
if (i > 0) row[h] = T.treeLeaves
|
||||
if (i < max) row[j] = T.treeLeaves
|
||||
|
||||
} else if (above === T.treeTrunk) {
|
||||
if (current === T.air) row[i] = T.treeTrunk
|
||||
else row[i] = T.treeRoot
|
||||
} else if (above === T.treeRoot) {
|
||||
row[i] = T.soil
|
||||
if (i > 0) row[h] = T.treeRoot
|
||||
if (i < max) row[j] = T.treeRoot
|
||||
}
|
||||
}
|
||||
|
||||
function ground(r: number, i: number, row: Block[], previousRow: Block[]) {
|
||||
function ground(r: number, i: number, current: Block, above: Block) {
|
||||
if (above === T.air) {
|
||||
if (r < P.soilHole) return T.air
|
||||
if (current === T.soil) return T.grass
|
||||
}
|
||||
return current
|
||||
}
|
||||
|
||||
function ground_(r: number, i: number, row: Block[], previousRow: Block[]) {
|
||||
const rootParts = [T.treeRootLeft, T.treeRootMiddle, T.treeRootRight]
|
||||
const prevBlock = previousRow[i]
|
||||
|
||||
|
@ -49,25 +56,39 @@ function ground(r: number, i: number, row: Block[], previousRow: Block[]) {
|
|||
}
|
||||
}
|
||||
|
||||
function rock(r: number, i: number, row: Block[], previousRow: Block[]) {
|
||||
if (previousRow[i] === T.soil && r < P.fray) row[i] = T.soil
|
||||
function rock(r: number, i: number, current: Block, above: Block) {
|
||||
if (above === T.soil && r < P.fray) return T.soil
|
||||
return current
|
||||
}
|
||||
|
||||
function underground(r: number, i: number, row: Block[], previousRow: Block[]) {
|
||||
if (previousRow[i] === T.stone && r < P.fray) row[i] = T.stone
|
||||
function underground(r: number, i: number, current: Block, above: Block) {
|
||||
if (above === T.stone && r < P.fray) return T.stone
|
||||
return current
|
||||
}
|
||||
|
||||
export default function createBlockExtender(rand: NoiseFunction2D) {
|
||||
function extendBlocks(level: number, column: number, row: Block[], previousRow: Block[]) {
|
||||
for (let i = 0; i < row.length; i++) {
|
||||
const r = rand(level, column + i)
|
||||
function growTrees(level: number, column: number, offset: number, row: Block[], previousRow: Block[]) {
|
||||
const r = rand(level, column + offset)
|
||||
trees(r, column, row, previousRow)
|
||||
}
|
||||
|
||||
if (level < L.ground) trees(r, i, row, previousRow)
|
||||
else if (level < L.rock) ground(r, i, row, previousRow)
|
||||
else if (level < L.underground) rock(r, i, row, previousRow)
|
||||
else underground(r, i, row, previousRow)
|
||||
function extendBlock(level: number, column: number, offset: number, current: Block, above: Block) {
|
||||
const r = rand(level, column + offset)
|
||||
|
||||
if (level < L.rock) return ground(r, column, current, above)
|
||||
if (level < L.underground) return rock(r, column, current, above)
|
||||
return underground(r, column, current, above)
|
||||
}
|
||||
|
||||
function extendRow(level: number, columnOffset: number, row: Block[], previousRow: Block[]) {
|
||||
for (let column = 0; column < row.length; column++) {
|
||||
if (level < L.ground) {
|
||||
// growTrees(level, column, columnOffset, row, previousRow)
|
||||
} else {
|
||||
row[column] = extendBlock(level, column, columnOffset, row[column], previousRow[column])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return extendBlocks
|
||||
return { extendBlock, extendRow }
|
||||
}
|
||||
|
|
|
@ -5,9 +5,7 @@ export default function createBlockGenerator(rand: NoiseFunction2D) {
|
|||
// randomly generate a block
|
||||
// level: number, smaller is "higher"
|
||||
// column: number, the x-axis
|
||||
// before: Block, the block type left of (before) this block
|
||||
// above: Block, the block type above this block
|
||||
const generateBlock = (level: number, column: number, before: Block, above: Block): Block => {
|
||||
const generateBlock = (level: number, column: number): Block => {
|
||||
// no randomness needed, there is always air above the trees
|
||||
if (level < L.treeTop) return T.air
|
||||
|
||||
|
@ -15,7 +13,7 @@ export default function createBlockGenerator(rand: NoiseFunction2D) {
|
|||
|
||||
// Air layer: mostly air, sometimes trees
|
||||
if (level < L.ground) {
|
||||
if (level === L.treeTop && r < P.tree) return T.treeTopMiddle
|
||||
if (level === L.treeTop && r < P.tree) return T.treeCrown
|
||||
return T.air
|
||||
}
|
||||
|
||||
|
@ -40,11 +38,11 @@ export default function createBlockGenerator(rand: NoiseFunction2D) {
|
|||
return T.bedrock
|
||||
}
|
||||
|
||||
const fillRow = (level: number, column: number, row: Block[], previousRow: Block[]) => {
|
||||
const fillRow = (level: number, column: number, row: Block[]) => {
|
||||
for (let i = 0; i < row.length; i++) {
|
||||
row[i] = generateBlock(level, column + i, row[i - 1], previousRow[i])
|
||||
row[i] = generateBlock(level, column + i)
|
||||
}
|
||||
}
|
||||
|
||||
return fillRow
|
||||
return { fillRow, generateBlock }
|
||||
}
|
||||
|
|
|
@ -12,48 +12,28 @@ export type Block = {
|
|||
hp: number,
|
||||
walkable: boolean,
|
||||
climbable?: boolean,
|
||||
transparent?: boolean,
|
||||
}
|
||||
|
||||
export const blockTypes: Record<string, Block> = {
|
||||
air: { type: 'air', hp: Infinity, walkable: true },
|
||||
air: { type: 'air', hp: Infinity, walkable: true, transparent: true },
|
||||
grass: { type: 'grass', hp: 1, walkable: false },
|
||||
|
||||
treeTopLeft: { type: 'treeTopLeft', hp: 5, walkable: true },
|
||||
treeTopMiddle: { type: 'treeTopMiddle', hp: 5, walkable: true },
|
||||
treeTopRight: { type: 'treeTopRight', hp: 5, walkable: true },
|
||||
|
||||
treeCrownLeft: { type: 'treeCrownLeft', hp: 5, walkable: true },
|
||||
treeCrownMiddle: { type: 'treeCrownMiddle', hp: 5, walkable: true, climbable: true },
|
||||
treeCrownRight: { type: 'treeCrownRight', hp: 5, walkable: true },
|
||||
|
||||
treeTrunkLeft: { type: 'treeTrunkLeft', hp: 5, walkable: true },
|
||||
treeTrunkMiddle: { type: 'treeTrunkMiddle', hp: 5, walkable: true, climbable: true },
|
||||
treeTrunkRight: { type: 'treeTrunkRight', hp: 5, walkable: true },
|
||||
|
||||
treeRootLeft: { type: 'treeRootLeft', hp: 5, walkable: true },
|
||||
treeRootMiddle: { type: 'treeRootMiddle', hp: 5, walkable: true, climbable: true },
|
||||
treeRootRight: { type: 'treeRootRight', hp: 5, walkable: true },
|
||||
|
||||
treeTopLeftMixed: { type: 'treeTopLeftMixed', hp: 5, walkable: true },
|
||||
treeCrownLeftMixed: { type: 'treeCrownLeftMixed', hp: 5, walkable: true },
|
||||
treeTrunkLeftMixed: { type: 'treeTrunkLeftMixed', hp: 5, walkable: true },
|
||||
treeRootLeftMixed: { type: 'treeRootLeftMixed', hp: 5, walkable: true },
|
||||
|
||||
treeTopRightMixed: { type: 'treeTopRightMixed', hp: 5, walkable: true },
|
||||
treeCrownRightMixed: { type: 'treeCrownRightMixed', hp: 5, walkable: true },
|
||||
treeTrunkRightMixed: { type: 'treeTrunkRightMixed', hp: 5, walkable: true },
|
||||
treeRootRightMixed: { type: 'treeRootRightMixed', hp: 5, walkable: true },
|
||||
treeCrown: { type: 'treeCrown', hp: 5, walkable: true, transparent: true },
|
||||
treeLeaves: { type: 'treeLeaves', hp: 5, walkable: true, transparent: true },
|
||||
treeTrunk: { type: 'treeTrunk', hp: 15, walkable: true, climbable: true, transparent: true },
|
||||
treeRoot: { type: 'treeRoot', hp: 15, walkable: true, climbable: true },
|
||||
|
||||
soil: { type: 'soil', hp: 2, walkable: false },
|
||||
soilGravel: { type: 'soilGravel', hp: 5, walkable: false },
|
||||
stoneGravel: { type: 'stoneGravel', hp: 5, walkable: false },
|
||||
stone: { type: 'stone', hp: 10, walkable: false },
|
||||
bedrock: { type: 'bedrock', hp: 25, walkable: false },
|
||||
cave: { type: 'cave', hp: Infinity, walkable: true },
|
||||
cave: { type: 'cave', hp: Infinity, walkable: true, transparent: true },
|
||||
}
|
||||
|
||||
export const level = {
|
||||
treeTop: 10,
|
||||
treeTop: 9,
|
||||
ground: 14,
|
||||
rock: 16,
|
||||
underground: 24,
|
||||
|
@ -61,7 +41,7 @@ export const level = {
|
|||
}
|
||||
|
||||
export const probability = {
|
||||
tree: 0.2,
|
||||
tree: 0.3,
|
||||
soilHole: 0.3,
|
||||
soilGravel: 0.2,
|
||||
stoneGravel: 0.1,
|
||||
|
|
|
@ -5,12 +5,19 @@ import createBlockExtender from './blockExt'
|
|||
|
||||
import {blockTypes as T, level as L, type Block} from './def'
|
||||
|
||||
export default function createLevel(width: number, height: number, seed = 'very random seed') {
|
||||
const MAX_LIGHT = 100 // maximum level where light shines
|
||||
|
||||
export default function createLevel(width: number, height: number, seed = 'extremely random seed') {
|
||||
const prng = alea(seed)
|
||||
const noise2D = createNoise2D(prng)
|
||||
const rand: NoiseFunction2D = (x, y) => 0.5 + 0.5 * noise2D(x, y)
|
||||
|
||||
// stores the current grid of blocks, visible on the screen
|
||||
const _grid: Block[][] = new Array(height)
|
||||
// stores the limit to where light still shines,
|
||||
// for each column currently visible on the screen
|
||||
const _lightBarrier: number[] = [...new Array(width)].map(() => MAX_LIGHT)
|
||||
|
||||
const blockGen = createBlockGenerator(rand)
|
||||
const blockExt = createBlockExtender(rand)
|
||||
|
||||
|
@ -19,23 +26,45 @@ export default function createLevel(width: number, height: number, seed = 'very
|
|||
// TODO
|
||||
}
|
||||
|
||||
function calcLightBarrier(columnOffset: number) {
|
||||
let previousBlock: Block = T.air
|
||||
|
||||
for (let col = 0; col < width; col++) {
|
||||
for (let level = 0; level < MAX_LIGHT; level++) {
|
||||
let block = blockGen.generateBlock(level, col + columnOffset)
|
||||
block = blockExt.extendBlock(level, col, columnOffset, block, previousBlock)
|
||||
previousBlock = block
|
||||
|
||||
if (!block.transparent) {
|
||||
_lightBarrier[col] = level
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function generate(column: number, y: number) {
|
||||
for (let i = 0; i < height; i++) {
|
||||
const level = y+i
|
||||
const row: Block[] = Array(width)
|
||||
const previousRow = i ? _grid[i-1] : [] as Block[]
|
||||
|
||||
blockGen(level, column, row, previousRow)
|
||||
blockExt(level, column, row, previousRow)
|
||||
blockGen.fillRow(level, column, row)
|
||||
blockExt.extendRow(level, column, row, previousRow)
|
||||
|
||||
_grid[i] = row
|
||||
}
|
||||
}
|
||||
|
||||
function sunLight(x: number, y: number) {
|
||||
calcLightBarrier(x)
|
||||
return _lightBarrier
|
||||
}
|
||||
|
||||
function grid(x: number, y: number) {
|
||||
generate(x, y)
|
||||
return _grid
|
||||
}
|
||||
|
||||
return { grid, change }
|
||||
return { grid, sunLight, change }
|
||||
}
|
||||
|
|
1
src/vite-env.d.ts
vendored
1
src/vite-env.d.ts
vendored
|
@ -13,6 +13,7 @@ declare global {
|
|||
name: string
|
||||
type: string
|
||||
icon: string
|
||||
amount: number
|
||||
quality?: 'bronze' | 'iron' | 'silver' | 'gold' | 'diamond'
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue