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 {
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 {$$items, slots} = instance;
if (cleared) {
@ -173,6 +173,7 @@ export default class Inventory extends Component {
instanceFromSchema() {
const Instance = super.instanceFromSchema();
const Component = this;
const {ecs} = Component;
return class InventoryInstance extends Instance {
$$items = {};
clear(slot) {
@ -180,6 +181,56 @@ export default class Inventory extends Component {
delete this.slots[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) {
return this.$$items[slot];
}
@ -265,7 +316,7 @@ export default class Inventory extends Component {
value: {
type: 'object',
properties: {
quantity: {type: 'uint16'},
qty: {type: 'uint16'},
source: {type: 'string'},
},
},

View File

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

View File

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

View File

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

View File

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

View File

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