fun: inventory + bag

This commit is contained in:
cha0s 2024-07-23 23:27:08 -05:00
parent f197658199
commit c45d24b909
15 changed files with 306 additions and 36 deletions

View File

@ -91,6 +91,9 @@ class ItemProxy {
get icon() {
return this.json.icon;
}
get label() {
return this.json.label;
}
get projection() {
return this.json.projection;
}

View File

@ -0,0 +1,54 @@
import styles from './bag.module.css';
import Slot from './slot.jsx';
/**
* Inventory bag. 10-40 slots of inventory.
*/
export default function Bag({
isInventoryOpen,
slots,
}) {
const Slots = slots.map((slot, i) => (
<div
className={
[styles.slotWrapper]
.filter(Boolean).join(' ')
}
key={i}
>
<Slot
icon={slot?.icon}
// onMouseDown={(event) => {
// onActivate(i)
// event.stopPropagation();
// }}
// onMouseUp={(event) => {
// event.stopPropagation();
// }}
// onDragOver={(event) => {
// event.preventDefault();
// }}
// onDragStart={(event) => {
// if (!slot) {
// event.preventDefault();
// }
// event.dataTransfer.setData('silphius/item', i);
// onActivate(i);
// }}
// onDrop={(event) => {
// event.preventDefault();
// onActivate(i);
// }}
qty={slot?.qty}
/>
</div>
));
return (
<div
className={styles.bag}
style={isInventoryOpen ? {transition: 'none'} : {left: '-440px'}}
>
{Slots}
</div>
);
}

View File

@ -0,0 +1,31 @@
.bag {
align-self: left;
--border: calc(var(--unit) * 3px);
background-color: rgba(02, 02, 28, 0.6);
border: var(--border) solid #444444;
box-sizing: border-box;
display: inline-block;
left: calc(var(--unit) * 20px);
line-height: 0;
position: absolute;
top: calc(var(--unit) * 90px);
transition: left 150ms;
max-width: 430.5px;
}
.slotWrapper {
border: var(--border) solid #999999;
box-sizing: border-box;
display: inline-block;
line-height: 0;
padding: 0;
&:not(:nth-child(10n)) {
border-right: none;
}
&:not(:nth-last-of-type(-n+10)) {
border-bottom: none;
}
&:hover {
background-color: rgba(0, 0, 0, 0.2);
}
}

View File

@ -9,8 +9,3 @@
user-select: text;
width: 100%;
}
@font-face {
font-family: "Cookbook";
src: url("/assets/fonts/Cookbook.woff") format("woff");
}

View File

@ -5,7 +5,7 @@
background-color: #00000044;
border: 1px solid #333333;
color: #ffffff;
font-family: "Cookbook";
font-family: Cookbook, Georgia, 'Times New Roman', Times, serif;
font-size: 16px;
margin: 4px;
padding: 0;

View File

@ -2,8 +2,3 @@
font-family: Cookbook, Georgia, 'Times New Roman', Times, serif;
font-size: 22px;
}
@font-face {
font-family: "Cookbook";
src: url("/assets/fonts/Cookbook.woff") format("woff");
}

View File

@ -4,7 +4,12 @@ import Slot from './slot.jsx';
/**
* The hotbar. 10 slots of inventory with an active selection.
*/
export default function Hotbar({active, onActivate, slots}) {
export default function Hotbar({
active,
hotbarIsHidden,
onActivate,
slots,
}) {
const Slots = slots.map((slot, i) => (
<div
className={
@ -43,7 +48,9 @@ export default function Hotbar({active, onActivate, slots}) {
return (
<div
className={styles.hotbar}
style={hotbarIsHidden ? {top: '-50px'} : {transition: 'none'}}
>
<p className={styles.label}>{slots[active] && slots[active].label}</p>
{Slots}
</div>
);

View File

@ -1,13 +1,32 @@
.hotbar {
align-self: center;
align-self: left;
--border: calc(var(--unit) * 3px);
background-color: rgba(0, 0, 0, 0.2);
background-color: rgba(02, 02, 57, 0.6);
border: var(--border) solid #444444;
box-sizing: border-box;
display: inline-block;
left: calc(var(--unit) * 20px);
line-height: 0;
position: absolute;
top: calc(var(--unit) * 25px);
top: calc(var(--unit) * 20px);
transition: top 150ms;
}
.label {
background-color: transparent;
color: white;
font-family: Cookbook, Georgia, 'Times New Roman', Times, serif;
left: 50%;
margin: 0;
position: absolute;
text-shadow:
0px -1px 0px black,
1px 0px 0px black,
0px 1px 0px black,
-1px 0px 0px black
;
top: -17.5px;
transform: translateX(-50%);
}
.slotWrapper {

View File

@ -11,6 +11,7 @@ import addKeyListener from './add-key-listener.js';
import ClientEcs from './client-ecs.js';
import Disconnected from './dom/disconnected.jsx';
import Chat from './dom/chat/chat.jsx';
import Bag from './dom/bag.jsx';
import Dom from './dom/dom.jsx';
import Entities from './dom/entities.jsx';
import HotBar from './dom/hotbar.jsx';
@ -18,6 +19,11 @@ import Pixi from './pixi/pixi.jsx';
import Devtools from './devtools.jsx';
import styles from './ui.module.css';
const KEY_MAP = {
keyDown: 1,
keyUp: 0,
};
function emptySlots() {
return Array(10).fill(undefined);
}
@ -50,6 +56,9 @@ function Ui({disconnected}) {
const [chatHistoryCaret, setChatHistoryCaret] = useState(-1);
const [chatMessages, setChatMessages] = useState({});
const [pendingMessage, setPendingMessage] = useState('');
const [hotbarIsHidden, setHotbarIsHidden] = useState(true);
const [hotbarHideHandle, setHotbarHideHandle] = useState();
const [isInventoryOpen, setIsInventoryOpen] = useState(false);
useEffect(() => {
async function setEcsStuff() {
const {default: Components} = await import('@/ecs/components/index.js');
@ -76,6 +85,44 @@ function Ui({disconnected}) {
clearTimeout(handle);
};
}, [disconnected]);
useEffect(() => {
return addKeyListener(document.body, ({type, payload}) => {
if (chatInputRef.current) {
chatInputRef.current.focus();
}
if (chatIsOpen) {
return;
}
let actionPayload;
switch (payload) {
case 'w': {
actionPayload = {type: 'moveUp', value: KEY_MAP[type]};
break;
}
case 'a': {
actionPayload = {type: 'moveLeft', value: KEY_MAP[type]};
break;
}
case 's': {
actionPayload = {type: 'moveDown', value: KEY_MAP[type]};
break;
}
case 'd': {
actionPayload = {type: 'moveRight', value: KEY_MAP[type]};
break;
}
}
if (actionPayload) {
client.send({
type: 'Action',
payload: actionPayload,
});
}
});
}, [
chatIsOpen,
client,
]);
useEffect(() => {
return addKeyListener(document.body, ({event, type, payload}) => {
if ('Escape' === payload && 'keyDown' === type && chatIsOpen) {
@ -88,10 +135,6 @@ function Ui({disconnected}) {
if (chatIsOpen) {
return;
}
const KEY_MAP = {
keyDown: 1,
keyUp: 0,
};
let actionPayload;
switch (payload) {
case '-':
@ -123,26 +166,25 @@ function Ui({disconnected}) {
}
break;
}
case 'w': {
actionPayload = {type: 'moveUp', value: KEY_MAP[type]};
break;
}
case 'a': {
actionPayload = {type: 'moveLeft', value: KEY_MAP[type]};
break;
}
case 's': {
actionPayload = {type: 'moveDown', value: KEY_MAP[type]};
break;
}
case 'd': {
actionPayload = {type: 'moveRight', value: KEY_MAP[type]};
break;
}
case ' ': {
actionPayload = {type: 'use', value: KEY_MAP[type]};
break;
}
case 'Tab': {
if ('keyDown' === type) {
if (isInventoryOpen) {
setHotbarIsHidden(true);
}
else {
setHotbarIsHidden(false);
if (hotbarHideHandle) {
clearTimeout(hotbarHideHandle);
}
}
setIsInventoryOpen(!isInventoryOpen);
}
break
}
case 'Enter': {
if ('keyDown' === type) {
setChatIsOpen(true);
@ -161,60 +203,150 @@ function Ui({disconnected}) {
}
case '1': {
if ('keyDown' === type) {
if (!isInventoryOpen) {
setHotbarIsHidden(false);
if (hotbarHideHandle) {
clearTimeout(hotbarHideHandle);
}
setHotbarHideHandle(setTimeout(() => {
setHotbarIsHidden(true);
}, 4000));
}
actionPayload = {type: 'changeSlot', value: 1};
}
break;
}
case '2': {
if ('keyDown' === type) {
if (!isInventoryOpen) {
setHotbarIsHidden(false);
if (hotbarHideHandle) {
clearTimeout(hotbarHideHandle);
}
setHotbarHideHandle(setTimeout(() => {
setHotbarIsHidden(true);
}, 4000));
}
actionPayload = {type: 'changeSlot', value: 2};
}
break;
}
case '3': {
if ('keyDown' === type) {
if (!isInventoryOpen) {
setHotbarIsHidden(false);
if (hotbarHideHandle) {
clearTimeout(hotbarHideHandle);
}
setHotbarHideHandle(setTimeout(() => {
setHotbarIsHidden(true);
}, 4000));
}
actionPayload = {type: 'changeSlot', value: 3};
}
break;
}
case '4': {
if ('keyDown' === type) {
if (!isInventoryOpen) {
setHotbarIsHidden(false);
if (hotbarHideHandle) {
clearTimeout(hotbarHideHandle);
}
setHotbarHideHandle(setTimeout(() => {
setHotbarIsHidden(true);
}, 4000));
}
actionPayload = {type: 'changeSlot', value: 4};
}
break;
}
case '5': {
if ('keyDown' === type) {
if (!isInventoryOpen) {
setHotbarIsHidden(false);
if (hotbarHideHandle) {
clearTimeout(hotbarHideHandle);
}
setHotbarHideHandle(setTimeout(() => {
setHotbarIsHidden(true);
}, 4000));
}
actionPayload = {type: 'changeSlot', value: 5};
}
break;
}
case '6': {
if ('keyDown' === type) {
if (!isInventoryOpen) {
setHotbarIsHidden(false);
if (hotbarHideHandle) {
clearTimeout(hotbarHideHandle);
}
setHotbarHideHandle(setTimeout(() => {
setHotbarIsHidden(true);
}, 4000));
}
actionPayload = {type: 'changeSlot', value: 6};
}
break;
}
case '7': {
if ('keyDown' === type) {
if (!isInventoryOpen) {
setHotbarIsHidden(false);
if (hotbarHideHandle) {
clearTimeout(hotbarHideHandle);
}
setHotbarHideHandle(setTimeout(() => {
setHotbarIsHidden(true);
}, 4000));
}
actionPayload = {type: 'changeSlot', value: 7};
}
break;
}
case '8': {
if ('keyDown' === type) {
if (!isInventoryOpen) {
setHotbarIsHidden(false);
if (hotbarHideHandle) {
clearTimeout(hotbarHideHandle);
}
setHotbarHideHandle(setTimeout(() => {
setHotbarIsHidden(true);
}, 4000));
}
actionPayload = {type: 'changeSlot', value: 8};
}
break;
}
case '9': {
if ('keyDown' === type) {
if (!isInventoryOpen) {
setHotbarIsHidden(false);
if (hotbarHideHandle) {
clearTimeout(hotbarHideHandle);
}
setHotbarHideHandle(setTimeout(() => {
setHotbarIsHidden(true);
}, 4000));
}
actionPayload = {type: 'changeSlot', value: 9};
}
break;
}
case '0': {
if ('keyDown' === type) {
if (!isInventoryOpen) {
setHotbarIsHidden(false);
if (hotbarHideHandle) {
clearTimeout(hotbarHideHandle);
}
setHotbarHideHandle(setTimeout(() => {
setHotbarIsHidden(true);
}, 4000));
}
actionPayload = {type: 'changeSlot', value: 10};
}
break;
@ -227,7 +359,17 @@ function Ui({disconnected}) {
});
}
});
}, [chatIsOpen, client, debug, devtoolsIsOpen, monopolizers, setDebug, setScale]);
}, [
chatIsOpen,
client,
debug,
devtoolsIsOpen,
hotbarHideHandle,
isInventoryOpen,
monopolizers,
setDebug,
setScale,
]);
usePacket('EcsChange', async () => {
setEcs(new ClientEcs({Components, Systems}));
setMainEntity(undefined);
@ -387,6 +529,15 @@ function Ui({disconnected}) {
event.preventDefault();
return;
}
if (!isInventoryOpen) {
setHotbarIsHidden(false);
if (hotbarHideHandle) {
clearTimeout(hotbarHideHandle);
}
setHotbarHideHandle(setTimeout(() => {
setHotbarIsHidden(true);
}, 4000));
}
if (event.deltaY > 0) {
client.send({
type: 'Action',
@ -411,6 +562,7 @@ function Ui({disconnected}) {
<Dom>
<HotBar
active={activeSlot}
hotbarIsHidden={hotbarIsHidden}
onActivate={(i) => {
client.send({
type: 'Action',
@ -419,6 +571,10 @@ function Ui({disconnected}) {
}}
slots={hotbarSlots}
/>
<Bag
isInventoryOpen={isInventoryOpen}
slots={Array(30).fill(undefined)}
/>
<Entities
camera={camera}
scale={scale}

View File

@ -26,3 +26,8 @@ body {
border-radius: 20px;
border: 3px solid #333;
}
@font-face {
font-family: "Cookbook";
src: url("/assets/fonts/Cookbook.woff") format("woff");
}

View File

@ -1,4 +1,5 @@
{
"icon": "/assets/brush/brush.png",
"label": "Brush",
"start": "/assets/brush/start.js"
}

View File

@ -1,5 +1,6 @@
{
"icon": "/assets/hoe/icon.png",
"label": "Hoe",
"projectionCheck": "/assets/hoe/projection-check.js",
"projection": {
"distance": [3, -1],

View File

@ -1,4 +1,5 @@
{
"icon": "/assets/potion/icon.png",
"label": "Potion",
"start": "/assets/potion/start.js"
}

View File

@ -1,5 +1,6 @@
{
"icon": "/assets/tomato-seeds/icon.png",
"label": "Tomato Seeds",
"projection": {
"distance": [1, -1],
"grid": [

View File

@ -1,5 +1,6 @@
{
"icon": "/assets/watering-can/icon.png",
"label": "Watering Can",
"projectionCheck": "/assets/watering-can/projection-check.js",
"projection": {
"distance": [3, -1],