feat: inventory ops

This commit is contained in:
cha0s 2024-09-29 05:25:54 -05:00
parent 997ef691ca
commit 9d9f94a7cc
6 changed files with 148 additions and 14 deletions

View File

@ -121,7 +121,7 @@ class ItemProxy {
export default class Inventory extends Component { export default class Inventory extends Component {
async updateMany(entities) { async updateMany(entities) {
for (const [id, {cleared, given, qtyUpdated, swapped}] of entities) { for (const [id, {cleared, distributed, given, qtyUpdated, swapped}] of entities) {
const instance = this.get(id); const instance = this.get(id);
const {$$items, slots} = instance; const {$$items, slots} = instance;
if (cleared) { if (cleared) {
@ -173,6 +173,7 @@ export default class Inventory extends Component {
instanceFromSchema() { instanceFromSchema() {
const Instance = super.instanceFromSchema(); const Instance = super.instanceFromSchema();
const Component = this; const Component = this;
const {ecs} = Component;
return class InventoryInstance extends Instance { return class InventoryInstance extends Instance {
$$items = {}; $$items = {};
clear(slot) { clear(slot) {
@ -180,6 +181,56 @@ export default class Inventory extends Component {
delete this.slots[slot]; delete this.slots[slot];
delete this.$$items[slot]; delete this.$$items[slot];
} }
async distribute(slot, potentialDestinations) {
const {slots} = this;
if (!slots[slot]) {
return;
}
const destinations = [];
const given = [];
const updated = [];
for (const [entityId, destination] of potentialDestinations) {
const {Inventory} = ecs.get(entityId);
const {slots} = Inventory;
if (!slots[destination]) {
slots[destination] = {
qty: 0,
source: slots[slot].source,
};
this.$$items[destination] = new ItemProxy(Component, this, destination);
await this.$$items[destination].load(slots[destination].source);
destinations.push([entityId, destination]);
given.push([entityId, destination]);
}
else if (slots[slot].source === slots[destination].source) {
destinations.push([entityId, destination]);
updated.push([entityId, destination, slots[destination].qty]);
}
}
const {qty} = slots[slot];
slots[slot].qty = 0;
for (let i = 0; i < qty; ++i) {
const [entityId, destination] = destinations[i % destinations.length];
const {Inventory} = ecs.get(entityId);
const {slots} = Inventory;
slots[destination].qty += 1;
}
if (!destinations.find(([entityId, destination]) => {
return entityId == this.entity && destination === slot;
})) {
this.clear(slot);
}
for (const [entityId, destination] of given) {
const {Inventory} = ecs.get(entityId);
const {slots} = Inventory;
Component.markChange(entityId, 'given', {[destination]: {qty: slots[destination].qty, source: slots[destination].source}});
}
for (const [entityId, destination, qty] of updated) {
const {Inventory} = ecs.get(entityId);
const {slots} = Inventory;
Component.markChange(entityId, 'qtyUpdated', {[destination]: slots[destination].qty - qty});
}
}
item(slot) { item(slot) {
return this.$$items[slot]; return this.$$items[slot];
} }
@ -265,7 +316,7 @@ export default class Inventory extends Component {
value: { value: {
type: 'object', type: 'object',
properties: { properties: {
quantity: {type: 'uint16'}, qty: {type: 'uint16'},
source: {type: 'string'}, source: {type: 'string'},
}, },
}, },

View File

@ -11,6 +11,8 @@ export default function Grid({
highlighted, highlighted,
label, label,
onSlotMouseDown, onSlotMouseDown,
onSlotMouseMove,
onSlotMouseUp,
slots, slots,
}) { }) {
const Slots = slots.map((slot, i) => ( const Slots = slots.map((slot, i) => (
@ -28,10 +30,15 @@ export default function Grid({
<Slot <Slot
icon={slot?.icon} icon={slot?.icon}
onMouseDown={(event) => { onMouseDown={(event) => {
onSlotMouseDown(i) onSlotMouseDown?.(i, event)
event.stopPropagation();
}}
onMouseMove={(event) => {
onSlotMouseMove?.(i, event)
event.stopPropagation(); event.stopPropagation();
}} }}
onMouseUp={(event) => { onMouseUp={(event) => {
onSlotMouseUp?.(i, event)
event.stopPropagation(); event.stopPropagation();
}} }}
onDragStart={(event) => { onDragStart={(event) => {

View File

@ -10,10 +10,9 @@ import Grid from './grid.jsx';
*/ */
function Hotbar({ function Hotbar({
active, active,
highlighted,
hotbarIsHidden, hotbarIsHidden,
onSlotMouseDown,
slots, slots,
...rest
}) { }) {
return ( return (
<div <div
@ -35,10 +34,9 @@ function Hotbar({
active={active} active={active}
color="rgba(02, 02, 57, 0.6)" color="rgba(02, 02, 57, 0.6)"
columns={10} columns={10}
highlighted={highlighted}
label={slots[active] && slots[active].label} label={slots[active] && slots[active].label}
onSlotMouseDown={onSlotMouseDown}
slots={slots} slots={slots}
{...rest}
/> />
</div> </div>
); );

View File

@ -11,6 +11,7 @@ export default function Slot({
onDragStart, onDragStart,
onDrop, onDrop,
onMouseDown, onMouseDown,
onMouseMove,
onMouseUp, onMouseUp,
qty = 1, qty = 1,
}) { }) {
@ -27,6 +28,7 @@ export default function Slot({
event.preventDefault(); event.preventDefault();
}} }}
onMouseDown={onMouseDown} onMouseDown={onMouseDown}
onMouseMove={onMouseMove}
onMouseUp={onMouseUp} onMouseUp={onMouseUp}
> >
<div <div

View File

@ -45,6 +45,8 @@ function Ui({disconnected}) {
const ecsRef = useEcs(); const ecsRef = useEcs();
const [showDisconnected, setShowDisconnected] = useState(false); const [showDisconnected, setShowDisconnected] = useState(false);
const [bufferSlot, setBufferSlot] = useState(); const [bufferSlot, setBufferSlot] = useState();
const hadBufferSlot = useRef();
const distributing = useRef(new Map());
const [devtoolsIsOpen, setDevtoolsIsOpen] = useState(false); const [devtoolsIsOpen, setDevtoolsIsOpen] = useState(false);
const ratio = (RESOLUTION.x * (devtoolsIsOpen ? 2 : 1)) / RESOLUTION.y; const ratio = (RESOLUTION.x * (devtoolsIsOpen ? 2 : 1)) / RESOLUTION.y;
const [camera, setCamera] = useState({x: 0, y: 0}); const [camera, setCamera] = useState({x: 0, y: 0});
@ -223,7 +225,7 @@ function Ui({disconnected}) {
}); });
} }
}); });
}, [client, keepHotbarOpen, setDebug]); }, [client, keepHotbarOpen, mainEntityRef, setDebug]);
const onEcsChangePacket = useCallback(() => { const onEcsChangePacket = useCallback(() => {
mainEntityRef.current = undefined; mainEntityRef.current = undefined;
monopolizers.current = []; monopolizers.current = [];
@ -390,7 +392,7 @@ function Ui({disconnected}) {
mainEntityRef, mainEntityRef,
scale, scale,
]); ]);
const hotbarOnSlotMouseDown = useCallback((i) => { const hotbarOnSlotMouseDown = useCallback((i, event) => {
keepHotbarOpen(); keepHotbarOpen();
if (trading) { if (trading) {
const index = losing.indexOf(i + 1); const index = losing.indexOf(i + 1);
@ -403,12 +405,77 @@ function Ui({disconnected}) {
setLosing([...losing]); setLosing([...losing]);
} }
else { else {
client.send({ hadBufferSlot.current = bufferSlot;
type: 'Action', switch (event.button) {
payload: {type: 'swapSlots', value: [0, mainEntityRef.current, i + 1]}, case 0:
}); if (bufferSlot) {
// ...
}
else {
client.send({
type: 'Action',
payload: {type: 'swapSlots', value: [0, mainEntityRef.current, i + 1]},
});
}
break;
case 2:
client.send({
type: 'Action',
payload: {
type: 'distribute',
value: [
i + 1,
[
[mainEntityRef.current, 0],
[mainEntityRef.current, i + 1],
],
],
},
});
break;
}
} }
}, [client, keepHotbarOpen, losing, mainEntityRef, trading]); }, [bufferSlot, client, keepHotbarOpen, losing, mainEntityRef, trading]);
const hotbarOnSlotMouseMove = useCallback((i) => {
if (hadBufferSlot.current) {
distributing.current.set([mainEntityRef.current, i + 1].join(':'), true);
}
}, [mainEntityRef]);
const hotbarOnSlotMouseUp = useCallback((i, event) => {
keepHotbarOpen();
if (trading) {
// ...
}
else {
switch (event.button) {
case 0:
if (distributing.current.size > 0) {
client.send({
type: 'Action',
payload: {
type: 'distribute',
value: [
0,
Array.from(distributing.current.keys()).map((idAndSlot) => idAndSlot.split(':')),
],
},
});
distributing.current.clear();
}
else if (hadBufferSlot.current) {
client.send({
type: 'Action',
payload: {type: 'swapSlots', value: [0, mainEntityRef.current, i + 1]},
});
}
else {
// ...
}
break;
}
}
hadBufferSlot.current = undefined;
}, [client, keepHotbarOpen, mainEntityRef, trading]);
const bagOnSlotMouseDown = useCallback((i) => { const bagOnSlotMouseDown = useCallback((i) => {
if (trading) { if (trading) {
const index = losing.indexOf(i + 11); const index = losing.indexOf(i + 11);
@ -574,6 +641,8 @@ function Ui({disconnected}) {
highlighted={trading && losing.filter((i) => i < 11).map((i) => i - 1)} highlighted={trading && losing.filter((i) => i < 11).map((i) => i - 1)}
hotbarIsHidden={hotbarIsHidden} hotbarIsHidden={hotbarIsHidden}
onSlotMouseDown={hotbarOnSlotMouseDown} onSlotMouseDown={hotbarOnSlotMouseDown}
onSlotMouseMove={hotbarOnSlotMouseMove}
onSlotMouseUp={hotbarOnSlotMouseUp}
slots={hotbarSlots} slots={hotbarSlots}
/> />
<Bag <Bag

View File

@ -260,6 +260,13 @@ export default class Engine {
Wallet.gold += earn; Wallet.gold += earn;
break; break;
} }
case 'distribute': {
if (!Controlled.locked) {
const [slot, destinations] = payload.value;
Inventory.distribute(slot, destinations);
}
break;
}
case 'swapSlots': { case 'swapSlots': {
if (!Controlled.locked) { if (!Controlled.locked) {
const [l, other, r] = payload.value; const [l, other, r] = payload.value;