selectable inventory items
This commit is contained in:
parent
c486c12283
commit
e4e3953734
7 changed files with 146 additions and 109 deletions
19
src/App.vue
19
src/App.vue
|
@ -15,6 +15,12 @@ const { player, direction, dx, dy } = usePlayer()
|
||||||
const { inputX, inputY, running, digging, paused, help, inventory } = useInput()
|
const { inputX, inputY, running, digging, paused, help, inventory } = useInput()
|
||||||
const level = createLevel(STAGE_WIDTH + 2, STAGE_HEIGHT + 2)
|
const level = createLevel(STAGE_WIDTH + 2, STAGE_HEIGHT + 2)
|
||||||
|
|
||||||
|
player.inventory.push(
|
||||||
|
{ name: 'Shovel', type: 'tool', icon: 'shovel', quality: 'bronze' },
|
||||||
|
{ name: 'Sword', type: 'weapon', icon: 'sword', quality: 'bronze' },
|
||||||
|
{ name: 'Pick Axe', type: 'tool', icon: 'pick', quality: 'bronze' },
|
||||||
|
)
|
||||||
|
|
||||||
let animationFrame = 0
|
let animationFrame = 0
|
||||||
let lastTick = 0
|
let lastTick = 0
|
||||||
|
|
||||||
|
@ -27,6 +33,7 @@ const ty = computed(() => (y.value - floorY.value) * -BLOCK_SIZE)
|
||||||
const rows = computed(() => level.grid(floorX.value, floorY.value))
|
const rows = computed(() => level.grid(floorX.value, floorY.value))
|
||||||
|
|
||||||
const walking = ref(false)
|
const walking = ref(false)
|
||||||
|
const inventorySelection = ref<InventoryItem | null>(null)
|
||||||
|
|
||||||
type Surroundings = {
|
type Surroundings = {
|
||||||
at: Block,
|
at: Block,
|
||||||
|
@ -125,7 +132,7 @@ onMounted(() => {
|
||||||
</template>
|
</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div id="player" :class="[direction, { walking }]">
|
<div id="player" :class="[direction, { walking }]" @click="inventory = !inventory">
|
||||||
<div class="head"></div>
|
<div class="head"></div>
|
||||||
<div class="body"></div>
|
<div class="body"></div>
|
||||||
<div class="legs">
|
<div class="legs">
|
||||||
|
@ -133,8 +140,9 @@ onMounted(() => {
|
||||||
<div class="right"></div>
|
<div class="right"></div>
|
||||||
</div>
|
</div>
|
||||||
<div class="arms">
|
<div class="arms">
|
||||||
<div class="left"></div>
|
<div v-if="inventorySelection"
|
||||||
<div class="right"></div>
|
:class="['item', `${inventorySelection.type}-${inventorySelection.icon}-${inventorySelection.quality}`]"
|
||||||
|
></div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div id="level-indicator">
|
<div id="level-indicator">
|
||||||
|
@ -143,7 +151,10 @@ onMounted(() => {
|
||||||
<template v-else>({{ clock }})</template>
|
<template v-else>({{ clock }})</template>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<Inventory :shown="inventory" :player="player" />
|
<Inventory :shown="inventory"
|
||||||
|
:items="player.inventory"
|
||||||
|
@selection="inventorySelection = $event"
|
||||||
|
/>
|
||||||
<Help v-show="help" />
|
<Help v-show="help" />
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
|
@ -34,67 +34,6 @@
|
||||||
|
|
||||||
.night .block, .night #player { filter: brightness(0.3) saturate(30%); }
|
.night .block, .night #player { filter: brightness(0.3) saturate(30%); }
|
||||||
|
|
||||||
#player {
|
|
||||||
--player-width: 64px;
|
|
||||||
--player-height: 76px;
|
|
||||||
position: absolute;
|
|
||||||
left: calc(var(--field-width) / 2);
|
|
||||||
top: calc(var(--field-height) / 2 - 10px);
|
|
||||||
display: flex;
|
|
||||||
flex-flow: column nowrap;
|
|
||||||
width: var(--player-width);
|
|
||||||
height: var(--player-height);
|
|
||||||
}
|
|
||||||
#player > div {
|
|
||||||
margin: auto;
|
|
||||||
background: transparent center no-repeat;
|
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
#player.right {
|
|
||||||
transform: scaleX(-1);
|
|
||||||
}
|
|
||||||
#player > .head {
|
|
||||||
width: 46px;
|
|
||||||
height: 46px;
|
|
||||||
background-image: url(/Characters/Alien/alien_head.png);
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
#player > .body {
|
|
||||||
width: 22px;
|
|
||||||
height: 24px;
|
|
||||||
margin-top: -8px;
|
|
||||||
background-image: url(/Characters/Alien/alien_body.png);
|
|
||||||
}
|
|
||||||
#player > .legs {
|
|
||||||
position: relative;
|
|
||||||
width: 14px;
|
|
||||||
height: 18px;
|
|
||||||
}
|
|
||||||
#player > .legs > div {
|
|
||||||
position: absolute;
|
|
||||||
width: 14px;
|
|
||||||
height: 18px;
|
|
||||||
background-image: url(/Characters/Alien/alien_leg.png);
|
|
||||||
transform-origin: top center;
|
|
||||||
}
|
|
||||||
#player.walking > .legs > div.right {
|
|
||||||
animation: dingle .3s linear infinite alternate;
|
|
||||||
}
|
|
||||||
#player.walking > .legs > div.left {
|
|
||||||
animation: dangle .3s linear infinite alternate;
|
|
||||||
}
|
|
||||||
#player > .arms {
|
|
||||||
position: absolute;
|
|
||||||
width: 8px;
|
|
||||||
height: 16px;
|
|
||||||
top: 48px;
|
|
||||||
left: 30px;
|
|
||||||
background-image: url(/Characters/Alien/alien_arm.png);
|
|
||||||
transform-origin: top center;
|
|
||||||
}
|
|
||||||
#player.walking > .arms {
|
|
||||||
animation: dangle .3s linear infinite alternate;
|
|
||||||
}
|
|
||||||
.block {
|
.block {
|
||||||
flex: 0 0 auto;
|
flex: 0 0 auto;
|
||||||
width: var(--block-size);
|
width: var(--block-size);
|
||||||
|
@ -113,12 +52,3 @@
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-flow: row wrap;
|
flex-flow: row wrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
@keyframes dingle {
|
|
||||||
from { transform: rotate(20deg); }
|
|
||||||
to { transform: rotate(-20deg); }
|
|
||||||
}
|
|
||||||
@keyframes dangle {
|
|
||||||
from { transform: rotate(-20deg); }
|
|
||||||
to { transform: rotate(20deg); }
|
|
||||||
}
|
|
||||||
|
|
9
src/assets/items.css
Normal file
9
src/assets/items.css
Normal file
|
@ -0,0 +1,9 @@
|
||||||
|
.item.tool-shovel-bronze {
|
||||||
|
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");
|
||||||
|
}
|
80
src/assets/player.css
Normal file
80
src/assets/player.css
Normal file
|
@ -0,0 +1,80 @@
|
||||||
|
#player {
|
||||||
|
--player-width: 64px;
|
||||||
|
--player-height: 76px;
|
||||||
|
position: absolute;
|
||||||
|
left: calc(var(--field-width) / 2);
|
||||||
|
top: calc(var(--field-height) / 2 - 10px);
|
||||||
|
display: flex;
|
||||||
|
flex-flow: column nowrap;
|
||||||
|
width: var(--player-width);
|
||||||
|
height: var(--player-height);
|
||||||
|
}
|
||||||
|
#player > div {
|
||||||
|
margin: auto;
|
||||||
|
background: transparent center no-repeat;
|
||||||
|
background-size: cover;
|
||||||
|
}
|
||||||
|
#player.right {
|
||||||
|
transform: scaleX(-1);
|
||||||
|
}
|
||||||
|
#player > .head {
|
||||||
|
width: 46px;
|
||||||
|
height: 46px;
|
||||||
|
background-image: url(/Characters/Alien/alien_head.png);
|
||||||
|
z-index: 1;
|
||||||
|
}
|
||||||
|
#player > .body {
|
||||||
|
width: 22px;
|
||||||
|
height: 24px;
|
||||||
|
margin-top: -8px;
|
||||||
|
background-image: url(/Characters/Alien/alien_body.png);
|
||||||
|
}
|
||||||
|
#player > .legs {
|
||||||
|
position: relative;
|
||||||
|
width: 14px;
|
||||||
|
height: 18px;
|
||||||
|
}
|
||||||
|
#player > .legs > div {
|
||||||
|
position: absolute;
|
||||||
|
width: 14px;
|
||||||
|
height: 18px;
|
||||||
|
background-image: url(/Characters/Alien/alien_leg.png);
|
||||||
|
transform-origin: top center;
|
||||||
|
}
|
||||||
|
#player.walking > .legs > div.right {
|
||||||
|
animation: dingle .3s linear infinite alternate;
|
||||||
|
}
|
||||||
|
#player.walking > .legs > div.left {
|
||||||
|
animation: dangle .3s linear infinite alternate;
|
||||||
|
}
|
||||||
|
#player > .arms {
|
||||||
|
position: absolute;
|
||||||
|
width: 8px;
|
||||||
|
height: 16px;
|
||||||
|
top: 48px;
|
||||||
|
left: 30px;
|
||||||
|
background-image: url(/Characters/Alien/alien_arm.png);
|
||||||
|
transform-origin: top center;
|
||||||
|
}
|
||||||
|
#player.walking > .arms {
|
||||||
|
animation: dangle .3s linear infinite alternate;
|
||||||
|
}
|
||||||
|
#player > .arms > .item {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
margin: -10px 0 0 -25px;
|
||||||
|
background-color: transparent;
|
||||||
|
background-position: center;
|
||||||
|
background-repeat: no-repeat;
|
||||||
|
background-size: contain;
|
||||||
|
transform: rotate(-90deg);
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes dingle {
|
||||||
|
from { transform: rotate(20deg); }
|
||||||
|
to { transform: rotate(-20deg); }
|
||||||
|
}
|
||||||
|
@keyframes dangle {
|
||||||
|
from { transform: rotate(-20deg); }
|
||||||
|
to { transform: rotate(20deg); }
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
import { createApp } from "vue";
|
import { createApp } from "vue";
|
||||||
import "./assets/field.css";
|
import "./assets/field.css";
|
||||||
|
import "./assets/player.css";
|
||||||
|
import "./assets/items.css";
|
||||||
import App from "./App.vue";
|
import App from "./App.vue";
|
||||||
|
|
||||||
createApp(App).mount("#app");
|
createApp(App).mount("#app");
|
||||||
|
|
|
@ -1,27 +1,29 @@
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
export interface Props {
|
export interface Props {
|
||||||
player: Player
|
items: InventoryItem[]
|
||||||
shown: boolean
|
shown: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
defineProps<Props>();
|
const props = defineProps<Props>();
|
||||||
|
const emit = defineEmits<{
|
||||||
|
(event: 'selection', value: InventoryItem | null): void
|
||||||
|
}>()
|
||||||
|
|
||||||
// inventory size is 12, and it is so empty
|
// inventory size is 15
|
||||||
const slots = ref([
|
const slots = Array(15)
|
||||||
{ name: 'Shovel', type: 'tool', icon: 'shovel', quality: 'bronze' },
|
const selectedIndex = ref(0)
|
||||||
{ name: 'Sword', type: 'weapon', icon: 'sword', quality: 'bronze' },
|
|
||||||
{ name: 'Pick Axe', type: 'tool', icon: 'pick', quality: 'bronze' },
|
const inventory = computed(() => {
|
||||||
null,
|
const inventory = [...props.items, ...slots]
|
||||||
null,
|
inventory.length = slots.length
|
||||||
null,
|
return inventory
|
||||||
null,
|
})
|
||||||
null,
|
|
||||||
null,
|
function setSelection(i: number) {
|
||||||
null,
|
selectedIndex.value = i
|
||||||
null,
|
emit('selection', inventory.value[i])
|
||||||
null,
|
}
|
||||||
])
|
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<template>
|
<template>
|
||||||
|
@ -31,14 +33,19 @@ const slots = ref([
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
<ol>
|
<ol>
|
||||||
<li v-for="item in slots"
|
<template v-for="(item,i) in inventory">
|
||||||
:class="[item?.type, item?.icon, item?.quality]"
|
<li v-if="!item" class="empty">
|
||||||
>
|
<i>(empty)</i>
|
||||||
<i v-if="item === null">(empty)</i>
|
</li>
|
||||||
<template v-else>
|
<li v-else
|
||||||
|
:class="['item', `${item.type}-${item.icon}-${item.quality}`, {
|
||||||
|
selected: selectedIndex === i
|
||||||
|
}]"
|
||||||
|
@click="setSelection(i)"
|
||||||
|
>
|
||||||
<b>{{ item.name }}</b>
|
<b>{{ item.name }}</b>
|
||||||
</template>
|
</li>
|
||||||
</li>
|
</template>
|
||||||
</ol>
|
</ol>
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
|
@ -71,6 +78,13 @@ li {
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
background: transparent center no-repeat;
|
background: transparent center no-repeat;
|
||||||
background-size: contain;
|
background-size: contain;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
li:not(.empty):hover, li.selected {
|
||||||
|
background-color: #FFCA;
|
||||||
|
}
|
||||||
|
li.empty {
|
||||||
|
cursor: not-allowed;
|
||||||
}
|
}
|
||||||
li > i, li > b {
|
li > i, li > b {
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -80,13 +94,4 @@ li > i, li > b {
|
||||||
background: black;
|
background: black;
|
||||||
font-size: .8em;
|
font-size: .8em;
|
||||||
}
|
}
|
||||||
.tool.shovel.bronze {
|
|
||||||
background-image: url("/Items/shovel_bronze.png");
|
|
||||||
}
|
|
||||||
.weapon.sword.bronze {
|
|
||||||
background-image: url("/Items/sword_bronze.png");
|
|
||||||
}
|
|
||||||
.tool.pick.bronze {
|
|
||||||
background-image: url("/Items/pick_bronze.png");
|
|
||||||
}
|
|
||||||
</style>
|
</style>
|
||||||
|
|
|
@ -7,7 +7,7 @@ const player: Player = reactive({
|
||||||
lastDir: 0,
|
lastDir: 0,
|
||||||
vx: 0,
|
vx: 0,
|
||||||
vy: 1, // always falling, because of gravity
|
vy: 1, // always falling, because of gravity
|
||||||
inventory: {}, // not yet in use
|
inventory: [], // not yet in use
|
||||||
})
|
})
|
||||||
|
|
||||||
export default function usePlayer() {
|
export default function usePlayer() {
|
||||||
|
|
Loading…
Add table
Reference in a new issue