You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

163 lines
4.3 KiB
Vue

<script setup lang="ts">
import { ref, computed, onMounted } from 'vue'
import { BLOCK_SIZE, STAGE_WIDTH, STAGE_HEIGHT, type Block } from './level/def'
import createLevel from './level'
import useTime from './util/useTime'
import useInput from './util/useInput'
import usePlayer from './util/usePlayer'
const { updateTime, timeOfDay, clock } = useTime()
const { player, direction, dx, dy } = usePlayer()
const { inputX, inputY, running, digging, paused, help } = useInput()
const level = createLevel(STAGE_WIDTH + 2, STAGE_HEIGHT + 2)
let animationFrame = 0
let lastTick = 0
let x = ref(0)
let y = ref(12)
const floorX = computed(() => Math.floor(x.value))
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))
type Surroundings = {
at: Block,
left: Block,
right: Block,
up: Block,
down: Block,
}
const surroundings = computed<Surroundings>(() => {
const px = player.x
const py = player.y
const row = rows.value
return {
at: row[py][px],
left: row[py][px - 1],
right: row[py][px + 1],
up: row[py - 1][px],
down: row[py + 1][px],
}
})
const blocked = computed(() => {
const { left, right, up, down } = surroundings.value
return {
left: !left.walkable,
right: !right.walkable,
up: !up.walkable,
down: !down.walkable,
}
})
function dig() {
console.warn('digging not yet implemented')
}
let lastTimeUpdate = 0
const move = (thisTick: number): void => {
animationFrame = requestAnimationFrame(move)
// do nothing when paused
if (paused.value) return
const tickDelta = thisTick - lastTick
lastTimeUpdate += tickDelta
// update in-game time every 60ms by 0.1
// then a day needs 10000 updates, and it takes about 10 minutes
if (lastTimeUpdate > 60) {
updateTime()
lastTimeUpdate = 0
}
6 years ago
player.vx = inputX.value
player.vy = inputY.value
6 years ago
if (inputX.value) player.lastDir = inputX.value
let dx_ = dx.value
let dy_ = dy.value
if (running.value) dx_ *= 2
if (dx_ > 0 && blocked.value.right) dx_ = 0
else if (dx_ < 0 && blocked.value.left) dx_ = 0
if (dy_ > 0 && blocked.value.down) dy_ = 0
else if (dy_ < 0 && blocked.value.up) dy_ = 0
if (!inputY.value && digging.value) {
dx_ = 0
dig()
6 years ago
}
const optimal = 16 // 16ms per tick => 60 FPS
const movementMultiplier = (tickDelta / optimal) * 2
x.value += dx_ * movementMultiplier
y.value += dy_ * movementMultiplier
lastTick = thisTick
6 years ago
}
onMounted(() => {
lastTick = performance.now()
move(lastTick)
})
6 years ago
</script>
<template>
<div id="field" :class="timeOfDay">
<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]" />
</template>
</div>
<div id="player" :class="direction" />
<div id="level-indicator">
x:{{ floorX }}, y:{{ floorY }}
<template v-if="paused">(PAUSED)</template>
<template v-else>({{ clock }})</template>
<div>{{ player.vx }}, {{ player.vy }}</div>
</div>
<div id="help" v-if="help">
<header>
<h1>How to play</h1>
</header>
<section>
<h2>Walk around: WASD or Arrow Keys</h2>
<p>A / Left: walk left</p>
<p>D / Right: walk right</p>
<p>W / Up: jump or climb up</p>
<p>S / Down: climb down</p>
<p>Hold Shift, to run.</p>
</section>
<section>
<h2>Dig Blocks: Left Mouse Key</h2>
<p>To dig a block, click on it with your left mouse key. Only adjacent blocks can be digged.</p>
<p><i>(not implemented, yet)</i></p>
</section>
<section>
<h2>Build / Set Blocks: Right Mouse Key</h2>
<p>To set a block, right click an empty position close to you.</p>
<p><i>(not implemented, yet)</i></p>
</section>
<section>
<h2>Inventory: I</h2>
<p>Press I to open the inventory and use the mouse to select an item. This item can then be put into the world with a right click.</p>
<p><i>(not implemented, yet)</i></p>
</section>
<p>&nbsp;</p>
<p>&nbsp;</p>
<p>&nbsp;</p>
</div>
</div>
</template>