diff --git a/app/ecs/components/inventory.js b/app/ecs/components/inventory.js
index 2dbb4cd..633efb1 100644
--- a/app/ecs/components/inventory.js
+++ b/app/ecs/components/inventory.js
@@ -91,6 +91,9 @@ class ItemProxy {
get icon() {
return this.json.icon;
}
+ get label() {
+ return this.json.label;
+ }
get projection() {
return this.json.projection;
}
diff --git a/app/react/components/dom/bag.jsx b/app/react/components/dom/bag.jsx
new file mode 100644
index 0000000..f4e979e
--- /dev/null
+++ b/app/react/components/dom/bag.jsx
@@ -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) => (
+
+ {
+ // 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}
+ />
+
+ ));
+ return (
+
+ {Slots}
+
+ );
+}
diff --git a/app/react/components/dom/bag.module.css b/app/react/components/dom/bag.module.css
new file mode 100644
index 0000000..ba2e9d1
--- /dev/null
+++ b/app/react/components/dom/bag.module.css
@@ -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);
+ }
+}
diff --git a/app/react/components/dom/chat/chat.module.css b/app/react/components/dom/chat/chat.module.css
index 83333f3..b5262fb 100644
--- a/app/react/components/dom/chat/chat.module.css
+++ b/app/react/components/dom/chat/chat.module.css
@@ -9,8 +9,3 @@
user-select: text;
width: 100%;
}
-
-@font-face {
- font-family: "Cookbook";
- src: url("/assets/fonts/Cookbook.woff") format("woff");
-}
diff --git a/app/react/components/dom/chat/input.module.css b/app/react/components/dom/chat/input.module.css
index 342c9bb..e3a9f2b 100644
--- a/app/react/components/dom/chat/input.module.css
+++ b/app/react/components/dom/chat/input.module.css
@@ -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;
diff --git a/app/react/components/dom/dialogues.module.css b/app/react/components/dom/dialogues.module.css
index cc4b6f2..8d6c086 100644
--- a/app/react/components/dom/dialogues.module.css
+++ b/app/react/components/dom/dialogues.module.css
@@ -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");
-}
diff --git a/app/react/components/dom/hotbar.jsx b/app/react/components/dom/hotbar.jsx
index 1ac516a..fdb4b08 100644
--- a/app/react/components/dom/hotbar.jsx
+++ b/app/react/components/dom/hotbar.jsx
@@ -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) => (
+
{slots[active] && slots[active].label}
{Slots}
);
diff --git a/app/react/components/dom/hotbar.module.css b/app/react/components/dom/hotbar.module.css
index 87cad00..77819a9 100644
--- a/app/react/components/dom/hotbar.module.css
+++ b/app/react/components/dom/hotbar.module.css
@@ -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 {
diff --git a/app/react/components/ui.jsx b/app/react/components/ui.jsx
index 8edb39e..02c2da4 100644
--- a/app/react/components/ui.jsx
+++ b/app/react/components/ui.jsx
@@ -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}) {
{
client.send({
type: 'Action',
@@ -419,6 +571,10 @@ function Ui({disconnected}) {
}}
slots={hotbarSlots}
/>
+