directional fixtures
This commit is contained in:
parent
72c5cd1d19
commit
3266ddb217
11 changed files with 106 additions and 46 deletions
BIN
public/Items/torchCeiling.png
Normal file
BIN
public/Items/torchCeiling.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 2.1 KiB |
BIN
public/Items/torchFloor.png
Normal file
BIN
public/Items/torchFloor.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 1.4 KiB |
BIN
public/Items/torchRight.png
Normal file
BIN
public/Items/torchRight.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.2 KiB |
75
src/App.vue
75
src/App.vue
|
@ -1,5 +1,5 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import type { Block, Direction, Item, InventoryItem } from './types.d'
|
import type { Block, BlockType, Direction, Item, InventoryItem, LightSource } from './types.d'
|
||||||
import { ref, computed, watch, onMounted, useTemplateRef } from 'vue'
|
import { ref, computed, watch, onMounted, useTemplateRef } from 'vue'
|
||||||
import Help from './screens/help.vue'
|
import Help from './screens/help.vue'
|
||||||
import Inventory from './screens/inventory.vue'
|
import Inventory from './screens/inventory.vue'
|
||||||
|
@ -25,7 +25,7 @@ let updateLightMap = (() => {}) as ReturnType<typeof useLightMask>
|
||||||
pocket(getItem('tool_shovel_wood'))
|
pocket(getItem('tool_shovel_wood'))
|
||||||
pocket(getItem('tool_sword_wood'))
|
pocket(getItem('tool_sword_wood'))
|
||||||
pocket(getItem('tool_pickaxe_wood'))
|
pocket(getItem('tool_pickaxe_wood'))
|
||||||
pocket(getItem('fixture_torch'))
|
pocket(getItem('fixture_torch'), 5)
|
||||||
|
|
||||||
let animationFrame = 0
|
let animationFrame = 0
|
||||||
let lastTick = 0
|
let lastTick = 0
|
||||||
|
@ -47,22 +47,40 @@ const lightBarrier = computed<number[]>(() => {
|
||||||
return level.sunLight(floorX.value)
|
return level.sunLight(floorX.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
const lightSources = computed(() => [])
|
|
||||||
|
|
||||||
const mapUpdateCount = ref(0)
|
const mapUpdateCount = ref(0)
|
||||||
const mapGrid = computed<Block[][]>(() => {
|
const mapGrid = computed<Block[][]>(() => {
|
||||||
const _update = mapUpdateCount.value // reactivity trigger
|
const _update = mapUpdateCount.value // reactivity trigger
|
||||||
return level.grid(floorX.value, floorY.value, true)
|
return level.grid(floorX.value, floorY.value, true)
|
||||||
})
|
})
|
||||||
|
const lightSources = computed(() => {
|
||||||
|
const _update = mapUpdateCount.value // reactivity trigger
|
||||||
|
const _floorX = floorX.value // reactivity trigger
|
||||||
|
const _floorY = floorY.value // reactivity trigger
|
||||||
|
|
||||||
|
const lightSources: LightSource[] = []
|
||||||
|
const grid = mapGrid.value
|
||||||
|
|
||||||
|
for (let y = 0; y < grid.length; y++) {
|
||||||
|
const row = grid[y]
|
||||||
|
for (let x = 0; x < row.length; x++) {
|
||||||
|
const block = row[x]
|
||||||
|
if (block.illumination) {
|
||||||
|
lightSources.push({
|
||||||
|
x, y,
|
||||||
|
strength: block.illumination,
|
||||||
|
color: block.color ?? '#FFE'
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return lightSources
|
||||||
|
})
|
||||||
|
|
||||||
const arriving = ref(true)
|
const arriving = ref(true)
|
||||||
const walking = ref(false)
|
const walking = ref(false)
|
||||||
const inventorySelection = ref<InventoryItem>(player.inventory[0])
|
const inventorySelection = ref<InventoryItem>(player.inventory[0])
|
||||||
|
|
||||||
const surroundings = computed<Record<Direction, Block>>(() => {
|
const getSurroundings = (x: number, y: number) => {
|
||||||
const _update = mapUpdateCount.value // reactivity trigger
|
|
||||||
const x = px.value
|
|
||||||
const y = py.value
|
|
||||||
const rows = mapGrid.value
|
const rows = mapGrid.value
|
||||||
|
|
||||||
const rowY = rows[y]
|
const rowY = rows[y]
|
||||||
|
@ -76,6 +94,11 @@ const surroundings = computed<Record<Direction, Block>>(() => {
|
||||||
up: rowYp[x],
|
up: rowYp[x],
|
||||||
down: rowYn[x],
|
down: rowYn[x],
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const surroundings = computed<Record<Direction, Block>>(() => {
|
||||||
|
const _update = mapUpdateCount.value // reactivity trigger
|
||||||
|
return getSurroundings(px.value, py.value)
|
||||||
})
|
})
|
||||||
const blocked = computed(() => {
|
const blocked = computed(() => {
|
||||||
const { left, right, up, down } = surroundings.value
|
const { left, right, up, down } = surroundings.value
|
||||||
|
@ -113,15 +136,26 @@ function dig(blockX: number, blockY: number, block: Block) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function build(blockX: number, blockY: number, block: InventoryItem) {
|
function build(blockX: number, blockY: number, block: InventoryItem) {
|
||||||
const blockToBuild = block.builds
|
let blockToBuild = block.builds
|
||||||
// the block doesn't do anything
|
if (!blockToBuild) return // the block doesn't do anything?!
|
||||||
if (!blockToBuild) return
|
|
||||||
|
// While blocks are just filling the space completely, fixtures are attached
|
||||||
|
// to the closest surface. We check the surroundings, starting at with left
|
||||||
|
// and right, then bottom and top.
|
||||||
|
if (block.type === 'fixture') {
|
||||||
|
const { left, right, up, down } = getSurroundings(blockX, blockY)
|
||||||
|
|
||||||
|
if (!left.transparent) blockToBuild = `${blockToBuild}Left`
|
||||||
|
else if (!right.transparent) blockToBuild = `${blockToBuild}Right`
|
||||||
|
else if (!up.transparent) blockToBuild = `${blockToBuild}Ceiling`
|
||||||
|
else if (!down.transparent) blockToBuild = `${blockToBuild}Floor`
|
||||||
|
}
|
||||||
|
|
||||||
level.change({
|
level.change({
|
||||||
change: 'exchange',
|
change: 'exchange',
|
||||||
x: floorX.value + blockX,
|
x: floorX.value + blockX,
|
||||||
y: floorY.value + blockY,
|
y: floorY.value + blockY,
|
||||||
newType: blockToBuild
|
newType: blockToBuild as BlockType
|
||||||
})
|
})
|
||||||
mapUpdateCount.value = mapUpdateCount.value + 1
|
mapUpdateCount.value = mapUpdateCount.value + 1
|
||||||
|
|
||||||
|
@ -130,10 +164,20 @@ function build(blockX: number, blockY: number, block: InventoryItem) {
|
||||||
}
|
}
|
||||||
|
|
||||||
function interactWith(blockX: number, blockY: number, block: Block) {
|
function interactWith(blockX: number, blockY: number, block: Block) {
|
||||||
if (debug) console.debug('interact with', blockX, blockY, block.type, inventorySelection.value.type, 'in hand')
|
if (debug) {
|
||||||
|
console.debug(
|
||||||
|
`interact with ${block.type} at ${blockX},${blockY},`,
|
||||||
|
`with a ${inventorySelection.value.id} in hand`
|
||||||
|
)
|
||||||
|
}
|
||||||
// § 4 ArbZG
|
// § 4 ArbZG
|
||||||
if (paused.value) return
|
if (paused.value) return
|
||||||
|
|
||||||
|
// no spooky interaction at a distance
|
||||||
|
const distanceX = ~~(px.value - blockX)
|
||||||
|
const distanceY = ~~(py.value - blockY)
|
||||||
|
if (distanceX > 1 || distanceY > 1) return
|
||||||
|
|
||||||
const blockInHand = inventorySelection.value
|
const blockInHand = inventorySelection.value
|
||||||
const shovelInHand = blockInHand.id.indexOf('shovel') >= 0
|
const shovelInHand = blockInHand.id.indexOf('shovel') >= 0
|
||||||
const pickaxeInHand = blockInHand.id.indexOf('pickaxe') >= 0
|
const pickaxeInHand = blockInHand.id.indexOf('pickaxe') >= 0
|
||||||
|
@ -144,8 +188,6 @@ function interactWith(blockX: number, blockY: number, block: Block) {
|
||||||
const canUseShovel = softTerrain.indexOf(block.type) >= 0
|
const canUseShovel = softTerrain.indexOf(block.type) >= 0
|
||||||
const canUsePickaxe = hardTerrain.indexOf(block.type) >= 0
|
const canUsePickaxe = hardTerrain.indexOf(block.type) >= 0
|
||||||
|
|
||||||
console.log({ canBuild, hasSpace, hasTool, blockInHand })
|
|
||||||
|
|
||||||
// put the selected block
|
// put the selected block
|
||||||
if (canBuild && hasSpace) {
|
if (canBuild && hasSpace) {
|
||||||
build(blockX, blockY, blockInHand)
|
build(blockX, blockY, blockInHand)
|
||||||
|
@ -227,8 +269,7 @@ onMounted(() => {
|
||||||
canvas.width = (BLOCK_SIZE + 2) * STAGE_WIDTH
|
canvas.width = (BLOCK_SIZE + 2) * STAGE_WIDTH
|
||||||
const ctx = canvas.getContext('2d')!
|
const ctx = canvas.getContext('2d')!
|
||||||
updateLightMap = useLightMask(
|
updateLightMap = useLightMask(
|
||||||
ctx, floorX, floorY,
|
ctx, floorY, tx, ty,
|
||||||
tx, ty, time,
|
|
||||||
lightBarrier, lightSources,
|
lightBarrier, lightSources,
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -98,7 +98,10 @@
|
||||||
.block.brickWall {
|
.block.brickWall {
|
||||||
background-image: url(/Tiles/brick_grey.png);
|
background-image: url(/Tiles/brick_grey.png);
|
||||||
}
|
}
|
||||||
.block.torch { background-image: url("/Items/torch.png"); }
|
.block.torchLeft { background-image: url("/Items/torchLeft.png"); }
|
||||||
|
.block.torchRight { background-image: url("/Items/torchRight.png"); }
|
||||||
|
.block.torchFloor { background-image: url("/Items/torchFloor.png"); }
|
||||||
|
.block.torchCeiling { background-image: url("/Items/torchCeiling.png"); }
|
||||||
|
|
||||||
#field {
|
#field {
|
||||||
user-select: none;
|
user-select: none;
|
||||||
|
|
|
@ -15,4 +15,4 @@
|
||||||
.item.block-stone { background-image: url("/Tiles/stone.png"); }
|
.item.block-stone { background-image: url("/Tiles/stone.png"); }
|
||||||
.item.block-gravel { background-image: url("/Tiles/gravel_stone.png"); }
|
.item.block-gravel { background-image: url("/Tiles/gravel_stone.png"); }
|
||||||
|
|
||||||
.item.fixture-torch { background-image: url("/Items/torch.png"); }
|
.item.fixture-torch { background-image: url("/Items/torchFloor.png"); }
|
||||||
|
|
|
@ -26,11 +26,18 @@ export const blockTypes: Record<BlockType, Block> = {
|
||||||
stone: { type: 'stone', hp: 10, walkable: false, drops: 'block_stone' },
|
stone: { type: 'stone', hp: 10, walkable: false, drops: 'block_stone' },
|
||||||
bedrock: { type: 'bedrock', hp: 25, walkable: false, drops: 'block_stone' },
|
bedrock: { type: 'bedrock', hp: 25, walkable: false, drops: 'block_stone' },
|
||||||
// Built Blocks
|
// Built Blocks
|
||||||
brickWall: { type: 'brickWall', hp: 25, walkable: false, drops: 'block_gravel', fixture: true },
|
brickWall: { type: 'brickWall', hp: 25, walkable: false, drops: 'block_gravel' },
|
||||||
torch: { type: 'torch', hp: 1, walkable: true, transparent: true, drops: 'fixture_torch', illuminated: true, fixture: true },
|
|
||||||
|
torchLeft: { type: 'torchLeft', hp: 1, walkable: true, transparent: true, drops: 'fixture_torch', illumination: 1.0, color: '#FFE', fixture: true },
|
||||||
|
torchRight: { type: 'torchRight', hp: 1, walkable: true, transparent: true, drops: 'fixture_torch', illumination: 1.0, color: '#FFE', fixture: true },
|
||||||
|
torchCeiling: { type: 'torchCeiling', hp: 1, walkable: true, transparent: true, drops: 'fixture_torch', illumination: 1.0, color: '#FFE', fixture: true },
|
||||||
|
torchFloor: { type: 'torchFloor', hp: 1, walkable: true, transparent: true, drops: 'fixture_torch', illumination: 1.0, color: '#FEB', fixture: true },
|
||||||
}
|
}
|
||||||
|
|
||||||
export const softTerrain: BlockType[] = ['grass', 'soil', 'soilGravel', 'torch']
|
export const softTerrain: BlockType[] = [
|
||||||
|
'grass', 'soil', 'soilGravel',
|
||||||
|
'torchLeft', 'torchRight', 'torchCeiling', 'torchFloor',
|
||||||
|
]
|
||||||
export const hardTerrain: BlockType[] = ['stone', 'stoneGravel', 'bedrock', 'brickWall']
|
export const hardTerrain: BlockType[] = ['stone', 'stoneGravel', 'bedrock', 'brickWall']
|
||||||
|
|
||||||
export const level = {
|
export const level = {
|
||||||
|
|
30
src/types.d.ts
vendored
30
src/types.d.ts
vendored
|
@ -1,4 +1,5 @@
|
||||||
import type { ItemId } from './level/items'
|
import type { ItemId } from './level/items'
|
||||||
|
export type { ItemId } from './level/items'
|
||||||
export type ItemQuality = 'wood' | 'iron' | 'diamond'
|
export type ItemQuality = 'wood' | 'iron' | 'diamond'
|
||||||
export type ItemType = 'tool' | 'block' | 'ore' | 'fixture'
|
export type ItemType = 'tool' | 'block' | 'ore' | 'fixture'
|
||||||
|
|
||||||
|
@ -8,7 +9,9 @@ export interface Item {
|
||||||
type: ItemType
|
type: ItemType
|
||||||
icon: string
|
icon: string
|
||||||
quality?: ItemQuality
|
quality?: ItemQuality
|
||||||
builds?: BlockType
|
// this should be ItemId | BlockType, but has to be string to avoid
|
||||||
|
// a circular type reference
|
||||||
|
builds?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface ToolItem extends Item {
|
export interface ToolItem extends Item {
|
||||||
|
@ -21,6 +24,8 @@ export interface BlockItem extends Item {
|
||||||
builds: BlockType
|
builds: BlockType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Fixture<T extends string> = `${T}Left` | `${T}Right` | `${T}Ceiling` | `${T}Floor`;
|
||||||
|
|
||||||
export type BlockType =
|
export type BlockType =
|
||||||
| 'air' | 'grass'
|
| 'air' | 'grass'
|
||||||
| 'treeCrown' | 'treeLeaves' | 'treeTrunk' | 'treeRoot'
|
| 'treeCrown' | 'treeLeaves' | 'treeTrunk' | 'treeRoot'
|
||||||
|
@ -28,17 +33,18 @@ export type BlockType =
|
||||||
| 'stone' | 'stoneGravel'
|
| 'stone' | 'stoneGravel'
|
||||||
| 'bedrock' | 'cave'
|
| 'bedrock' | 'cave'
|
||||||
| 'brickWall'
|
| 'brickWall'
|
||||||
| 'torch'
|
| Fixture<'torch'>
|
||||||
|
|
||||||
export type Block = {
|
export type Block = {
|
||||||
type: BlockType, // what is it?
|
type: BlockType // what is it?
|
||||||
hp: number, // how long do I need to hit it?
|
hp: number // how long do I need to hit it?
|
||||||
walkable: boolean, // can I walk through it?
|
walkable: boolean // can I walk through it?
|
||||||
climbable?: boolean, // can I climb it?
|
climbable?: boolean // can I climb it?
|
||||||
transparent?: boolean, // can I see through it?
|
transparent?: boolean // can I see through it?
|
||||||
illuminated?: boolean, // is it glowing?
|
fixture?: boolean // is it built by the player?
|
||||||
fixture?: boolean, // is it built by the player?
|
illumination?: number // How many blocks wide is it glowing?
|
||||||
drops?: ItemId, // what do I get, when loot it?
|
color?: string // How is it coloured?
|
||||||
|
drops?: ItemId // what do I get, when loot it?
|
||||||
}
|
}
|
||||||
|
|
||||||
// describes a changed block, eg digged or placed by the player
|
// describes a changed block, eg digged or placed by the player
|
||||||
|
@ -54,11 +60,10 @@ type ChangedBlock = {
|
||||||
y: number
|
y: number
|
||||||
newType: BlockType
|
newType: BlockType
|
||||||
}
|
}
|
||||||
type Change = DamagedBlock | ChangedBlock
|
export type Change = DamagedBlock | ChangedBlock
|
||||||
|
|
||||||
export interface InventoryItem extends Item {
|
export interface InventoryItem extends Item {
|
||||||
amount: number
|
amount: number
|
||||||
quality: ItemQuality | null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Moveable {
|
export interface Moveable {
|
||||||
|
@ -85,4 +90,5 @@ export interface LightSource {
|
||||||
x: number
|
x: number
|
||||||
y: number
|
y: number
|
||||||
strength: number
|
strength: number
|
||||||
|
color: string
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,11 +6,9 @@ type RefOrComputed<T> = Ref<T> | ComputedRef<T>
|
||||||
|
|
||||||
export default function useLightMask(
|
export default function useLightMask(
|
||||||
ctx: CanvasRenderingContext2D,
|
ctx: CanvasRenderingContext2D,
|
||||||
x: RefOrComputed<number>,
|
|
||||||
y: RefOrComputed<number>,
|
y: RefOrComputed<number>,
|
||||||
tx: RefOrComputed<number>,
|
tx: RefOrComputed<number>,
|
||||||
ty: RefOrComputed<number>,
|
ty: RefOrComputed<number>,
|
||||||
time: RefOrComputed<number>,
|
|
||||||
lightBarrier: RefOrComputed<number[]>,
|
lightBarrier: RefOrComputed<number[]>,
|
||||||
lightSources: RefOrComputed<LightSource[]>,
|
lightSources: RefOrComputed<LightSource[]>,
|
||||||
) {
|
) {
|
||||||
|
@ -24,8 +22,6 @@ export default function useLightMask(
|
||||||
const playerLightSize = B * 1.8
|
const playerLightSize = B * 1.8
|
||||||
|
|
||||||
function drawPlayerLight(sizeMul:number) {
|
function drawPlayerLight(sizeMul:number) {
|
||||||
const t = time.value
|
|
||||||
|
|
||||||
const playerLight = ctx.createRadialGradient(
|
const playerLight = ctx.createRadialGradient(
|
||||||
playerX - tx.value, playerY - ty.value, 0,
|
playerX - tx.value, playerY - ty.value, 0,
|
||||||
playerX - tx.value, playerY - ty.value, playerLightSize * sizeMul
|
playerX - tx.value, playerY - ty.value, playerLightSize * sizeMul
|
||||||
|
@ -43,11 +39,19 @@ export default function useLightMask(
|
||||||
for (const src of lightSources.value) {
|
for (const src of lightSources.value) {
|
||||||
const x = src.x * B
|
const x = src.x * B
|
||||||
const y = src.y * B
|
const y = src.y * B
|
||||||
|
const strength = src.strength + (src.strength * Math.random()) / 10
|
||||||
|
const light = ctx.createRadialGradient(
|
||||||
|
x + BHalf, y - BHalf, 0,
|
||||||
|
x + BHalf, y - BHalf, strength * B,
|
||||||
|
)
|
||||||
|
const color = src.color
|
||||||
|
|
||||||
const light = ctx.createRadialGradient(x, y, 0, x, y, src.strength)
|
light.addColorStop(0.0, color)
|
||||||
light.addColorStop(0.0, "#CC8F")
|
light.addColorStop(0.4, `${color}A`)
|
||||||
light.addColorStop(0.4, "#CC8A")
|
light.addColorStop(1, `${color}0`)
|
||||||
light.addColorStop(1, "#CC80")
|
|
||||||
|
ctx.fillStyle = light
|
||||||
|
ctx.fillRect(0, 0, W, H)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ const player = reactive<Player>({
|
||||||
inventory: [],
|
inventory: [],
|
||||||
})
|
})
|
||||||
|
|
||||||
const pocket = (newItem: Item) => {
|
const pocket = (newItem: Item, amount = 1) => {
|
||||||
const existing = player.inventory.find(item => item.name === newItem.name)
|
const existing = player.inventory.find(item => item.name === newItem.name)
|
||||||
|
|
||||||
if (existing) {
|
if (existing) {
|
||||||
|
@ -19,9 +19,8 @@ const pocket = (newItem: Item) => {
|
||||||
return existing.amount
|
return existing.amount
|
||||||
}
|
}
|
||||||
player.inventory.push({
|
player.inventory.push({
|
||||||
quality: null,
|
...newItem,
|
||||||
amount: 1,
|
amount,
|
||||||
...newItem
|
|
||||||
})
|
})
|
||||||
return 1
|
return 1
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue