block drops into inventory
This commit is contained in:
parent
9d3912f98f
commit
eee378bb7b
6 changed files with 80 additions and 43 deletions
36
src/App.vue
36
src/App.vue
|
@ -4,6 +4,7 @@ import Help from './screens/help.vue'
|
||||||
import Inventory from './screens/inventory.vue'
|
import Inventory from './screens/inventory.vue'
|
||||||
|
|
||||||
import { BLOCK_SIZE, STAGE_WIDTH, STAGE_HEIGHT, type Block, blockTypes } from './level/def'
|
import { BLOCK_SIZE, STAGE_WIDTH, STAGE_HEIGHT, type Block, blockTypes } from './level/def'
|
||||||
|
import { getItem, getItemClass } from './level/items'
|
||||||
import createLevel from './level'
|
import createLevel from './level'
|
||||||
|
|
||||||
import useTime from './util/useTime'
|
import useTime from './util/useTime'
|
||||||
|
@ -12,18 +13,16 @@ import usePlayer from './util/usePlayer'
|
||||||
import useLightMap from './util/useLightMap'
|
import useLightMap from './util/useLightMap'
|
||||||
|
|
||||||
const { updateTime, time, timeOfDay, clock } = useTime()
|
const { updateTime, time, timeOfDay, clock } = useTime()
|
||||||
const { player, direction, dx, dy } = usePlayer()
|
const { player, direction, dx, dy, pocket } = usePlayer()
|
||||||
const { inputX, inputY, running, paused, help, inventory } = useInput()
|
const { inputX, inputY, running, paused, help, inventory } = useInput()
|
||||||
const level = createLevel(STAGE_WIDTH + 2, STAGE_HEIGHT + 2)
|
const level = createLevel(STAGE_WIDTH + 2, STAGE_HEIGHT + 2)
|
||||||
|
|
||||||
const lightMapEl = ref<HTMLCanvasElement | undefined>(undefined)
|
const lightMapEl = ref<HTMLCanvasElement | undefined>(undefined)
|
||||||
let updateLightMap: ReturnType<typeof useLightMap>
|
let updateLightMap: ReturnType<typeof useLightMap>
|
||||||
|
|
||||||
player.inventory.push(
|
pocket({ name: 'Shovel', type: 'tool', icon: 'shovel', quality: 'bronze' })
|
||||||
{ name: 'Shovel', type: 'tool', icon: 'shovel', quality: 'bronze', amount: 1 },
|
pocket({ name: 'Sword', type: 'weapon', icon: 'sword', quality: 'bronze' })
|
||||||
{ name: 'Sword', type: 'weapon', icon: 'sword', quality: 'bronze', amount: 2 },
|
pocket({ name: 'Pick Axe', type: 'tool', icon: 'pick', quality: 'bronze' })
|
||||||
{ name: 'Pick Axe', type: 'tool', icon: 'pick', quality: 'bronze', amount: 1 },
|
|
||||||
)
|
|
||||||
|
|
||||||
let animationFrame = 0
|
let animationFrame = 0
|
||||||
let lastTick = 0
|
let lastTick = 0
|
||||||
|
@ -74,15 +73,20 @@ const blocked = computed(() => {
|
||||||
// TODO
|
// TODO
|
||||||
const damagedBlocks = ref([])
|
const damagedBlocks = ref([])
|
||||||
|
|
||||||
function dig(blockX: number, blockY: number, oldBlockType: BlockType) {
|
function dig(blockX: number, blockY: number, block: Block) {
|
||||||
// § 4 ArbZG
|
// § 4 ArbZG
|
||||||
if (paused.value) return
|
if (paused.value) return
|
||||||
|
|
||||||
// TODO: temporary filter
|
// air cannot be digged, I guess
|
||||||
if (oldBlockType === 'air' || oldBlockType === 'cave') return
|
if (block.type === 'air' || block.type === 'cave') return
|
||||||
// when we finally dig that block
|
// can only dig in player proximity
|
||||||
|
if (Math.abs(player.x - blockX) > 2 || Math.abs(player.y - blockY) > 2) return
|
||||||
|
// finally dig that block
|
||||||
|
// TODO: damage blocks first
|
||||||
level.change({ type: 'exchange', x: floorX.value + blockX, y: floorY.value + blockY, newType: 'air' })
|
level.change({ type: 'exchange', x: floorX.value + blockX, y: floorY.value + blockY, newType: 'air' })
|
||||||
|
|
||||||
|
if (block.drops) pocket(getItem(block.drops))
|
||||||
|
|
||||||
// This feels like cheating, but it makes Vue recalculate floorX
|
// This feels like cheating, but it makes Vue recalculate floorX
|
||||||
// which then recalculates the blocks, so that the changes are
|
// which then recalculates the blocks, so that the changes are
|
||||||
// applied. Otherwise, they wouldn't be visible before moving
|
// applied. Otherwise, they wouldn't be visible before moving
|
||||||
|
@ -165,6 +169,12 @@ function calcBrightness(level: number, row: number) {
|
||||||
return `sun-${delta}`
|
return `sun-${delta}`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function selectTool(item: InventoryItem) {
|
||||||
|
// only tools and weapons can be selected
|
||||||
|
// if (item.type !== 'tool' && item.type !== 'weapon') return
|
||||||
|
inventorySelection.value = item
|
||||||
|
}
|
||||||
|
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
const canvas = lightMapEl.value!
|
const canvas = lightMapEl.value!
|
||||||
canvas.height = (BLOCK_SIZE + 2) * STAGE_HEIGHT
|
canvas.height = (BLOCK_SIZE + 2) * STAGE_HEIGHT
|
||||||
|
@ -184,7 +194,7 @@ onMounted(() => {
|
||||||
<template v-for="(row, y) in rows">
|
<template v-for="(row, y) in rows">
|
||||||
<div v-for="(block, x) in row"
|
<div v-for="(block, x) in row"
|
||||||
:class="['block', block.type]"
|
:class="['block', block.type]"
|
||||||
@click="dig(x, y, block.type)"
|
@click="dig(x, y, block)"
|
||||||
/>
|
/>
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
@ -198,7 +208,7 @@ onMounted(() => {
|
||||||
</div>
|
</div>
|
||||||
<div class="arms">
|
<div class="arms">
|
||||||
<div v-if="inventorySelection"
|
<div v-if="inventorySelection"
|
||||||
:class="['item', `${inventorySelection.type}-${inventorySelection.icon}-${inventorySelection.quality}`]"
|
:class="['item', getItemClass(inventorySelection)]"
|
||||||
></div>
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -213,7 +223,7 @@ onMounted(() => {
|
||||||
|
|
||||||
<Inventory :shown="inventory"
|
<Inventory :shown="inventory"
|
||||||
:items="player.inventory"
|
:items="player.inventory"
|
||||||
@selection="inventorySelection = $event"
|
@selection="selectTool($event)"
|
||||||
/>
|
/>
|
||||||
<Help v-show="help" />
|
<Help v-show="help" />
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,9 +1,5 @@
|
||||||
.item.tool-shovel-bronze {
|
.item.tool-shovel-bronze { background-image: url("/Items/shovel_bronze.png"); }
|
||||||
background-image: url("/Items/shovel_bronze.png");
|
.item.weapon-sword-bronze { background-image: url("/Items/sword_bronze.png"); }
|
||||||
}
|
.item.tool-pick-bronze { background-image: url("/Items/pick_bronze.png"); }
|
||||||
.item.weapon-sword-bronze {
|
|
||||||
background-image: url("/Items/sword_bronze.png");
|
.item.block-dirt { background-image: url("/Tiles/dirt.png"); }
|
||||||
}
|
|
||||||
.item.tool-pick-bronze {
|
|
||||||
background-image: url("/Items/pick_bronze.png");
|
|
||||||
}
|
|
||||||
|
|
|
@ -8,12 +8,13 @@ export const STAGE_HEIGHT = 12 // 12*64 = 768 pixel high stage
|
||||||
export const GRAVITY = 10 // blocks per second
|
export const GRAVITY = 10 // blocks per second
|
||||||
|
|
||||||
export type Block = {
|
export type Block = {
|
||||||
type: string,
|
type: string, // what is it?
|
||||||
hp: number,
|
hp: number, // how long do I need to hit it?
|
||||||
walkable: boolean,
|
walkable: boolean, // can I walk through it?
|
||||||
climbable?: boolean,
|
climbable?: boolean, // can I climb it?
|
||||||
transparent?: boolean,
|
transparent?: boolean, // can I see through it?
|
||||||
illuminated?: boolean,
|
illuminated?: boolean, // is it glowing?
|
||||||
|
drops?: string, // what do I get, when loot it?
|
||||||
}
|
}
|
||||||
|
|
||||||
export type BlockType =
|
export type BlockType =
|
||||||
|
@ -23,19 +24,19 @@ export type BlockType =
|
||||||
| 'bedrock' | 'cave'
|
| 'bedrock' | 'cave'
|
||||||
|
|
||||||
export const blockTypes: Record<BlockType, Block> = {
|
export const blockTypes: Record<BlockType, Block> = {
|
||||||
air: { type: 'air', hp: Infinity, walkable: true, transparent: true, illuminated: true },
|
air: { type: 'air', hp: Infinity, walkable: true, transparent: true },
|
||||||
grass: { type: 'grass', hp: 5, walkable: false },
|
grass: { type: 'grass', hp: 5, walkable: false, drops: 'dirt' },
|
||||||
|
|
||||||
treeCrown: { type: 'treeCrown', hp: 1, walkable: true, transparent: true },
|
treeCrown: { type: 'treeCrown', hp: 1, walkable: true, transparent: true, drops: 'leaves' },
|
||||||
treeLeaves: { type: 'treeLeaves', hp: 1, walkable: true, transparent: true },
|
treeLeaves: { type: 'treeLeaves', hp: 1, walkable: true, transparent: true, drops: 'leaves' },
|
||||||
treeTrunk: { type: 'treeTrunk', hp: 10, walkable: true, climbable: true, transparent: true },
|
treeTrunk: { type: 'treeTrunk', hp: 10, walkable: true, climbable: true, transparent: true, drops: 'wood' },
|
||||||
treeRoot: { type: 'treeRoot', hp: 10, walkable: true, climbable: true },
|
treeRoot: { type: 'treeRoot', hp: 10, walkable: true, climbable: true, drops: 'wood' },
|
||||||
|
|
||||||
soil: { type: 'soil', hp: 5, walkable: false },
|
soil: { type: 'soil', hp: 5, walkable: false, drops: 'dirt' },
|
||||||
soilGravel: { type: 'soilGravel', hp: 5, walkable: false },
|
soilGravel: { type: 'soilGravel', hp: 5, walkable: false, drops: 'gravel' },
|
||||||
stoneGravel: { type: 'stoneGravel', hp: 10, walkable: false },
|
stoneGravel: { type: 'stoneGravel', hp: 10, walkable: false, drops: 'gravel' },
|
||||||
stone: { type: 'stone', hp: 10, walkable: false },
|
stone: { type: 'stone', hp: 10, walkable: false, drops: 'stone' },
|
||||||
bedrock: { type: 'bedrock', hp: 25, walkable: false },
|
bedrock: { type: 'bedrock', hp: 25, walkable: false, drops: 'stone' },
|
||||||
cave: { type: 'cave', hp: Infinity, walkable: true, transparent: true },
|
cave: { type: 'cave', hp: Infinity, walkable: true, transparent: true },
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,9 +1,10 @@
|
||||||
import type { BlockType } from './def'
|
import type { BlockType } from './def'
|
||||||
|
import type { InventoryItem } from '../util/usePlayer'
|
||||||
|
|
||||||
export type ItemQuality = 'wood' | 'iron' | 'silver' | 'gold' | 'diamond'
|
export type ItemQuality = 'wood' | 'iron' | 'silver' | 'gold' | 'diamond'
|
||||||
export type ItemType = 'tool' | 'weapon'
|
export type ItemType = 'tool' | 'weapon' | 'block' | 'ore'
|
||||||
|
|
||||||
export interface Item = {
|
export interface Item {
|
||||||
name: string
|
name: string
|
||||||
type: ItemType
|
type: ItemType
|
||||||
icon: string
|
icon: string
|
||||||
|
@ -19,6 +20,7 @@ export const items: Item[] = [
|
||||||
{ name: 'dirt', type: 'block', icon: 'dirt', hasQuality: false },
|
{ name: 'dirt', type: 'block', icon: 'dirt', hasQuality: false },
|
||||||
{ name: 'wood', type: 'block', icon: 'wood', hasQuality: false },
|
{ name: 'wood', type: 'block', icon: 'wood', hasQuality: false },
|
||||||
{ name: 'stone', type: 'block', icon: 'stone', hasQuality: false },
|
{ name: 'stone', type: 'block', icon: 'stone', hasQuality: false },
|
||||||
|
{ name: 'gravel', type: 'block', icon: 'stone', hasQuality: false }, // TODO
|
||||||
|
|
||||||
{ name: 'coal', type: 'ore', icon: 'ore_coal', hasQuality: false },
|
{ name: 'coal', type: 'ore', icon: 'ore_coal', hasQuality: false },
|
||||||
{ name: 'iron', type: 'ore', icon: 'ore_iron', hasQuality: false },
|
{ name: 'iron', type: 'ore', icon: 'ore_iron', hasQuality: false },
|
||||||
|
@ -36,3 +38,18 @@ export const damage: Record<ItemQuality, number> = {
|
||||||
gold: 5,
|
gold: 5,
|
||||||
diamond: 8,
|
diamond: 8,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getItem(name: string, quality = null) {
|
||||||
|
const item = items.find(i => i.name === name)
|
||||||
|
if (item) {
|
||||||
|
return {
|
||||||
|
...item,
|
||||||
|
quality,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getItemClass(item: InventoryItem) {
|
||||||
|
if (item.quality) return `${item.type}-${item.icon}-${item.quality}`
|
||||||
|
return `${item.type}-${item.icon}`
|
||||||
|
}
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
|
import { getItemClass } from '../level/items'
|
||||||
|
import type { InventoryItem } from '../util/usePlayer'
|
||||||
|
|
||||||
export interface Props {
|
export interface Props {
|
||||||
items: InventoryItem[]
|
items: InventoryItem[]
|
||||||
|
@ -39,7 +41,7 @@ function setSelection(i: number) {
|
||||||
<i>(empty)</i>
|
<i>(empty)</i>
|
||||||
</li>
|
</li>
|
||||||
<li v-else
|
<li v-else
|
||||||
:class="['item', `${item.type}-${item.icon}-${item.quality}`, {
|
:class="['item', getItemClass(item), {
|
||||||
selected: selectedIndex === i
|
selected: selectedIndex === i
|
||||||
}]"
|
}]"
|
||||||
@click="setSelection(i)"
|
@click="setSelection(i)"
|
||||||
|
|
|
@ -20,10 +20,21 @@ const player = reactive<Player>({
|
||||||
inventory: [], // not yet in use
|
inventory: [], // not yet in use
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const pocket = (newItem: InventoryItem) => {
|
||||||
|
const existing = player.inventory.find(item => item.name === newItem.name)
|
||||||
|
|
||||||
|
if (existing) existing.amount += 1
|
||||||
|
else player.inventory.push({
|
||||||
|
quality: null,
|
||||||
|
amount: 1,
|
||||||
|
...newItem
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
export default function usePlayer() {
|
export default function usePlayer() {
|
||||||
const dx = computed(() => player.vx * RECIPROCAL)
|
const dx = computed(() => player.vx * RECIPROCAL)
|
||||||
const dy = computed(() => player.vy * RECIPROCAL)
|
const dy = computed(() => player.vy * RECIPROCAL)
|
||||||
const direction = computed(() => (player.lastDir < 0 ? 'left' : 'right'))
|
const direction = computed(() => (player.lastDir < 0 ? 'left' : 'right'))
|
||||||
|
|
||||||
return { player, direction, dx, dy }
|
return { player, direction, dx, dy, pocket }
|
||||||
}
|
}
|
||||||
|
|
Loading…
Add table
Reference in a new issue