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">
|
||||
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 Help from './screens/help.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_sword_wood'))
|
||||
pocket(getItem('tool_pickaxe_wood'))
|
||||
pocket(getItem('fixture_torch'))
|
||||
pocket(getItem('fixture_torch'), 5)
|
||||
|
||||
let animationFrame = 0
|
||||
let lastTick = 0
|
||||
|
@ -47,22 +47,40 @@ const lightBarrier = computed<number[]>(() => {
|
|||
return level.sunLight(floorX.value)
|
||||
})
|
||||
|
||||
const lightSources = computed(() => [])
|
||||
|
||||
const mapUpdateCount = ref(0)
|
||||
const mapGrid = computed<Block[][]>(() => {
|
||||
const _update = mapUpdateCount.value // reactivity trigger
|
||||
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 walking = ref(false)
|
||||
const inventorySelection = ref<InventoryItem>(player.inventory[0])
|
||||
|
||||
const surroundings = computed<Record<Direction, Block>>(() => {
|
||||
const _update = mapUpdateCount.value // reactivity trigger
|
||||
const x = px.value
|
||||
const y = py.value
|
||||
const getSurroundings = (x: number, y: number) => {
|
||||
const rows = mapGrid.value
|
||||
|
||||
const rowY = rows[y]
|
||||
|
@ -76,6 +94,11 @@ const surroundings = computed<Record<Direction, Block>>(() => {
|
|||
up: rowYp[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 { 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) {
|
||||
const blockToBuild = block.builds
|
||||
// the block doesn't do anything
|
||||
if (!blockToBuild) return
|
||||
let blockToBuild = block.builds
|
||||
if (!blockToBuild) return // the block doesn't do anything?!
|
||||
|
||||
// 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({
|
||||
change: 'exchange',
|
||||
x: floorX.value + blockX,
|
||||
y: floorY.value + blockY,
|
||||
newType: blockToBuild
|
||||
newType: blockToBuild as BlockType
|
||||
})
|
||||
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) {
|
||||
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
|
||||
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 shovelInHand = blockInHand.id.indexOf('shovel') >= 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 canUsePickaxe = hardTerrain.indexOf(block.type) >= 0
|
||||
|
||||
console.log({ canBuild, hasSpace, hasTool, blockInHand })
|
||||
|
||||
// put the selected block
|
||||
if (canBuild && hasSpace) {
|
||||
build(blockX, blockY, blockInHand)
|
||||
|
@ -227,8 +269,7 @@ onMounted(() => {
|
|||
canvas.width = (BLOCK_SIZE + 2) * STAGE_WIDTH
|
||||
const ctx = canvas.getContext('2d')!
|
||||
updateLightMap = useLightMask(
|
||||
ctx, floorX, floorY,
|
||||
tx, ty, time,
|
||||
ctx, floorY, tx, ty,
|
||||
lightBarrier, lightSources,
|
||||
)
|
||||
} else {
|
||||
|
|
|
@ -98,7 +98,10 @@
|
|||
.block.brickWall {
|
||||
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 {
|
||||
user-select: none;
|
||||
|
|
|
@ -15,4 +15,4 @@
|
|||
.item.block-stone { background-image: url("/Tiles/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' },
|
||||
bedrock: { type: 'bedrock', hp: 25, walkable: false, drops: 'block_stone' },
|
||||
// Built Blocks
|
||||
brickWall: { type: 'brickWall', hp: 25, walkable: false, drops: 'block_gravel', fixture: true },
|
||||
torch: { type: 'torch', hp: 1, walkable: true, transparent: true, drops: 'fixture_torch', illuminated: true, fixture: true },
|
||||
brickWall: { type: 'brickWall', hp: 25, walkable: false, drops: 'block_gravel' },
|
||||
|
||||
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 level = {
|
||||
|
|
30
src/types.d.ts
vendored
30
src/types.d.ts
vendored
|
@ -1,4 +1,5 @@
|
|||
import type { ItemId } from './level/items'
|
||||
export type { ItemId } from './level/items'
|
||||
export type ItemQuality = 'wood' | 'iron' | 'diamond'
|
||||
export type ItemType = 'tool' | 'block' | 'ore' | 'fixture'
|
||||
|
||||
|
@ -8,7 +9,9 @@ export interface Item {
|
|||
type: ItemType
|
||||
icon: string
|
||||
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 {
|
||||
|
@ -21,6 +24,8 @@ export interface BlockItem extends Item {
|
|||
builds: BlockType
|
||||
}
|
||||
|
||||
type Fixture<T extends string> = `${T}Left` | `${T}Right` | `${T}Ceiling` | `${T}Floor`;
|
||||
|
||||
export type BlockType =
|
||||
| 'air' | 'grass'
|
||||
| 'treeCrown' | 'treeLeaves' | 'treeTrunk' | 'treeRoot'
|
||||
|
@ -28,17 +33,18 @@ export type BlockType =
|
|||
| 'stone' | 'stoneGravel'
|
||||
| 'bedrock' | 'cave'
|
||||
| 'brickWall'
|
||||
| 'torch'
|
||||
| Fixture<'torch'>
|
||||
|
||||
export type Block = {
|
||||
type: BlockType, // what is it?
|
||||
hp: number, // how long do I need to hit it?
|
||||
walkable: boolean, // can I walk through it?
|
||||
climbable?: boolean, // can I climb it?
|
||||
transparent?: boolean, // can I see through it?
|
||||
illuminated?: boolean, // is it glowing?
|
||||
fixture?: boolean, // is it built by the player?
|
||||
drops?: ItemId, // what do I get, when loot it?
|
||||
type: BlockType // what is it?
|
||||
hp: number // how long do I need to hit it?
|
||||
walkable: boolean // can I walk through it?
|
||||
climbable?: boolean // can I climb it?
|
||||
transparent?: boolean // can I see through it?
|
||||
fixture?: boolean // is it built by the player?
|
||||
illumination?: number // How many blocks wide is it glowing?
|
||||
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
|
||||
|
@ -54,11 +60,10 @@ type ChangedBlock = {
|
|||
y: number
|
||||
newType: BlockType
|
||||
}
|
||||
type Change = DamagedBlock | ChangedBlock
|
||||
export type Change = DamagedBlock | ChangedBlock
|
||||
|
||||
export interface InventoryItem extends Item {
|
||||
amount: number
|
||||
quality: ItemQuality | null
|
||||
}
|
||||
|
||||
export interface Moveable {
|
||||
|
@ -85,4 +90,5 @@ export interface LightSource {
|
|||
x: number
|
||||
y: number
|
||||
strength: number
|
||||
color: string
|
||||
}
|
||||
|
|
|
@ -6,11 +6,9 @@ type RefOrComputed<T> = Ref<T> | ComputedRef<T>
|
|||
|
||||
export default function useLightMask(
|
||||
ctx: CanvasRenderingContext2D,
|
||||
x: RefOrComputed<number>,
|
||||
y: RefOrComputed<number>,
|
||||
tx: RefOrComputed<number>,
|
||||
ty: RefOrComputed<number>,
|
||||
time: RefOrComputed<number>,
|
||||
lightBarrier: RefOrComputed<number[]>,
|
||||
lightSources: RefOrComputed<LightSource[]>,
|
||||
) {
|
||||
|
@ -24,8 +22,6 @@ export default function useLightMask(
|
|||
const playerLightSize = B * 1.8
|
||||
|
||||
function drawPlayerLight(sizeMul:number) {
|
||||
const t = time.value
|
||||
|
||||
const playerLight = ctx.createRadialGradient(
|
||||
playerX - tx.value, playerY - ty.value, 0,
|
||||
playerX - tx.value, playerY - ty.value, playerLightSize * sizeMul
|
||||
|
@ -43,11 +39,19 @@ export default function useLightMask(
|
|||
for (const src of lightSources.value) {
|
||||
const x = src.x * 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, "#CC8F")
|
||||
light.addColorStop(0.4, "#CC8A")
|
||||
light.addColorStop(1, "#CC80")
|
||||
light.addColorStop(0.0, color)
|
||||
light.addColorStop(0.4, `${color}A`)
|
||||
light.addColorStop(1, `${color}0`)
|
||||
|
||||
ctx.fillStyle = light
|
||||
ctx.fillRect(0, 0, W, H)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,7 +11,7 @@ const player = reactive<Player>({
|
|||
inventory: [],
|
||||
})
|
||||
|
||||
const pocket = (newItem: Item) => {
|
||||
const pocket = (newItem: Item, amount = 1) => {
|
||||
const existing = player.inventory.find(item => item.name === newItem.name)
|
||||
|
||||
if (existing) {
|
||||
|
@ -19,9 +19,8 @@ const pocket = (newItem: Item) => {
|
|||
return existing.amount
|
||||
}
|
||||
player.inventory.push({
|
||||
quality: null,
|
||||
amount: 1,
|
||||
...newItem
|
||||
...newItem,
|
||||
amount,
|
||||
})
|
||||
return 1
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue