feat: shop

This commit is contained in:
cha0s 2024-09-27 07:45:13 -05:00
parent 512c5a470a
commit f673833a1d
27 changed files with 476 additions and 58 deletions

View File

@ -97,21 +97,25 @@ class ItemProxy {
get projection() { get projection() {
return this.json.projection; return this.json.projection;
} }
get price() {
return this.json.price;
}
get qty() { get qty() {
return this.instance.slots[this.slot].qty; return this.instance.slots[this.slot].qty;
} }
set qty(qty) { set qty(qty) {
const {instance} = this; const {instance} = this;
if (qty <= 0) { if (qty <= 0) {
this.Component.markChange(instance.entity, 'cleared', {[this.slot]: true}); instance.clear(this.slot);
delete instance.slots[this.slot];
delete instance.$$items[this.slot];
} }
else { else {
instance.slots[this.slot].qty = qty; instance.slots[this.slot].qty = qty;
this.Component.markChange(instance.entity, 'qtyUpdated', {[this.slot]: qty}); this.Component.markChange(instance.entity, 'qtyUpdated', {[this.slot]: qty});
} }
} }
get source() {
return this.instance.slots[this.slot].source;
}
} }
export default class Inventory extends Component { export default class Inventory extends Component {
@ -169,6 +173,11 @@ export default class Inventory extends Component {
const Component = this; const Component = this;
return class InventoryInstance extends Instance { return class InventoryInstance extends Instance {
$$items = {}; $$items = {};
clear(slot) {
Component.markChange(this.entity, 'cleared', {[slot]: true});
delete this.slots[slot];
delete this.$$items[slot];
}
item(slot) { item(slot) {
return this.$$items[slot]; return this.$$items[slot];
} }
@ -185,7 +194,7 @@ export default class Inventory extends Component {
if (!slots[slot]) { if (!slots[slot]) {
slots[slot] = stack; slots[slot] = stack;
this.$$items[slot] = new ItemProxy(Component, this, slot); this.$$items[slot] = new ItemProxy(Component, this, slot);
await this.$$items[slot].load(); await this.$$items[slot].load(stack.source);
Component.markChange(this.entity, 'given', {[slot]: slots[slot]}); Component.markChange(this.entity, 'given', {[slot]: slots[slot]});
return; return;
} }

View File

@ -0,0 +1,3 @@
import Component from '@/ecs/component.js';
export default class Shop extends Component {}

View File

@ -0,0 +1,7 @@
import Component from '@/ecs/component.js';
export default class Wallet extends Component {
static properties = {
gold: {type: 'uint32'},
};
}

View File

@ -1,6 +1,7 @@
import {memo} from 'react'; import {memo} from 'react';
import styles from './bag.module.css'; import styles from './bag.module.css';
import gridStyles from './grid.module.css';
import Grid from './grid.jsx'; import Grid from './grid.jsx';
@ -8,8 +9,9 @@ import Grid from './grid.jsx';
* Inventory bag. 10-40 slots of inventory. * Inventory bag. 10-40 slots of inventory.
*/ */
function Bag({ function Bag({
highlighted,
isInventoryOpen, isInventoryOpen,
onActivate, onSlotMouseDown,
slots, slots,
}) { }) {
return ( return (
@ -17,11 +19,20 @@ function Bag({
className={styles.bag} className={styles.bag}
style={isInventoryOpen ? {transition: 'opacity 50ms'} : {opacity: 0, left: '-440px'}} style={isInventoryOpen ? {transition: 'opacity 50ms'} : {opacity: 0, left: '-440px'}}
> >
<style>{`
.${styles.bag} .${gridStyles.highlighted} {
background-color: rgba(255, 0, 0, 0.4);
}
.${styles.bag} .${gridStyles.highlighted} button {
border: 2.5px dashed rgba(255, 255, 255, 0.4);
}
`}</style>
<Grid <Grid
color="rgba(02, 02, 28, 0.6)" color="rgba(02, 02, 28, 0.6)"
columns={10} columns={10}
highlighted={highlighted}
label="Bag" label="Bag"
onActivate={onActivate} onSlotMouseDown={onSlotMouseDown}
slots={slots} slots={slots}
/> />
</div> </div>

View File

@ -1,6 +1,7 @@
import {memo} from 'react'; import {memo} from 'react';
import styles from './external.module.css'; import styles from './external.module.css';
import gridStyles from './grid.module.css';
import Grid from './grid.jsx'; import Grid from './grid.jsx';
@ -8,8 +9,9 @@ import Grid from './grid.jsx';
* External inventory. * External inventory.
*/ */
function External({ function External({
highlighted,
isInventoryOpen, isInventoryOpen,
onActivate, onSlotMouseDown,
slots, slots,
}) { }) {
return ( return (
@ -17,11 +19,20 @@ function External({
className={styles.external} className={styles.external}
style={isInventoryOpen ? {transition: 'opacity 50ms'} : {opacity: 0, top: '450px'}} style={isInventoryOpen ? {transition: 'opacity 50ms'} : {opacity: 0, top: '450px'}}
> >
<style>{`
.${styles.external} .${gridStyles.highlighted} {
background-color: rgba(0, 255, 0, 0.4);
}
.${styles.external} .${gridStyles.highlighted} button {
border: 2.5px dashed rgba(0, 0, 0, 0.4);
}
`}</style>
<Grid <Grid
color="rgba(57, 02, 02, 0.6)" color="rgba(57, 02, 02, 0.6)"
columns={10} columns={10}
highlighted={highlighted}
label="Chest" label="Chest"
onActivate={onActivate} onSlotMouseDown={onSlotMouseDown}
slots={slots} slots={slots}
/> />
</div> </div>

View File

@ -1,7 +1,3 @@
.external { .external {
left: 20px; --nothing: 0;
opacity: 1;
position: absolute;
top: 274px;
transition: top 150ms, opacity 200ms;
} }

View File

@ -8,14 +8,19 @@ export default function Grid({
active = -1, active = -1,
color, color,
columns, columns,
highlighted,
label, label,
onActivate, onSlotMouseDown,
slots, slots,
}) { }) {
const Slots = slots.map((slot, i) => ( const Slots = slots.map((slot, i) => (
<div <div
className={ className={
[styles.slot, active === i && styles.active] [
styles.slot,
active === i && styles.active,
highlighted && highlighted.includes(i) && styles.highlighted,
]
.filter(Boolean).join(' ') .filter(Boolean).join(' ')
} }
key={i} key={i}
@ -23,25 +28,14 @@ export default function Grid({
<Slot <Slot
icon={slot?.icon} icon={slot?.icon}
onMouseDown={(event) => { onMouseDown={(event) => {
onActivate(i) onSlotMouseDown(i)
event.stopPropagation(); event.stopPropagation();
}} }}
onMouseUp={(event) => { onMouseUp={(event) => {
event.stopPropagation(); event.stopPropagation();
}} }}
onDragOver={(event) => {
event.preventDefault();
}}
onDragStart={(event) => { onDragStart={(event) => {
if (!slot) {
event.preventDefault(); event.preventDefault();
}
event.dataTransfer.setData('silphius/item', i);
onActivate(i);
}}
onDrop={(event) => {
event.preventDefault();
onActivate(i);
}} }}
qty={slot?.qty} qty={slot?.qty}
/> />

View File

@ -43,3 +43,7 @@
z-index: 1; z-index: 1;
} }
} }
.highlighted {
--nothing: 0;
}

View File

@ -10,8 +10,9 @@ import Grid from './grid.jsx';
*/ */
function Hotbar({ function Hotbar({
active, active,
highlighted,
hotbarIsHidden, hotbarIsHidden,
onActivate, onSlotMouseDown,
slots, slots,
}) { }) {
return ( return (
@ -23,13 +24,20 @@ function Hotbar({
.${styles.hotbar} .${gridStyles.label} { .${styles.hotbar} .${gridStyles.label} {
text-align: center; text-align: center;
} }
.${styles.hotbar} .${gridStyles.highlighted} {
background-color: rgba(255, 0, 0, 0.4);
}
.${styles.hotbar} .${gridStyles.highlighted} button {
border: 2.5px dashed rgba(255, 255, 255, 0.4);
}
`}</style> `}</style>
<Grid <Grid
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}
onActivate={onActivate} onSlotMouseDown={onSlotMouseDown}
slots={slots} slots={slots}
/> />
</div> </div>

View File

@ -0,0 +1,86 @@
import styles from './trade.module.css';
function reducePrice(r, item) {
return r + item.price * item.qty;
}
function Trade({
isInventoryOpen,
losing: losingUnfiltered,
onTradeAccepted,
gaining: gainingUnfiltered,
wallet,
}) {
const losing = losingUnfiltered.filter(Boolean);
const gaining = gainingUnfiltered.filter(Boolean);
const nop = 0 === losing.length && 0 === gaining.length;
let earn = losing.reduce(reducePrice, 0) - gaining.reduce(reducePrice, 0);
const canAfford = -earn <= wallet;
return (
<div
className={styles.trade}
style={isInventoryOpen ? {transition: 'opacity 50ms'} : {opacity: 0, top: '450px'}}
>
<div className={styles.tradeInner}>
{
nop
? (
<div className={styles.nop}>Let&apos;s trade!</div>
)
: (
<ul className={styles.summary}>
{losing.length > 0 && (
<li className={styles.lose}>
Shop receives:
{' '}
<ul className={styles.items}>
{losing.map(({label, qty}, i) => (
<li key={i}>{label} x<span className={styles.qty}>{qty}</span></li>
))}
</ul>
</li>
)}
{gaining.length > 0 && (
<li className={styles.gain}>
You receive:
{' '}
<ul className={styles.items}>
{gaining.map(({label, qty}, i) => (
<li key={i}>{label} x<span className={styles.qty}>{qty}</span></li>
))}
</ul>
</li>
)}
<li className={styles.subtotal}>
{
earn > 0
? (
<span className={styles.cost}>You <span className={styles.make}>earn <span className={styles.amount}>{earn}</span></span><span className={styles.gold}>🄶</span></span>
)
: (
earn < 0
? <span className={styles.cost}>You <span className={styles.pay}>pay <span className={styles.amount}>{-earn}</span></span><span className={styles.gold}>🄶</span></span>
: false
)
}
<button
className={styles.agree}
disabled={!canAfford}
onClick={(event) => {
event.stopPropagation();
onTradeAccepted(event);
}}
title={canAfford ? "Finish the transaction" : `Transaction requires ${-earn}g but you only have ${wallet}g`}
>
{canAfford ? "Agree" : "Can't afford"}
</button>
</li>
</ul>
)
}
</div>
</div>
);
}
export default Trade;

View File

@ -0,0 +1,129 @@
.trade {
border: 2.5px solid #444444;
flex-grow: 1;
font-size: 0.8em;
margin: 16px 40px 0 8px;
text-shadow:
1px 0 0 black,
0 1px 0 black,
-1px 0 0 black,
0 -1px 0 black
;
}
.tradeInner {
background-color: rgba(0, 0, 0, 0.7);
border: 2.5px solid #999999;
display: flex;
font-family: Cookbook, Georgia, 'Times New Roman', Times, serif;
height: 100%;
padding: 2.5px;
position: relative;
width: 100%;
ul {
list-style: none;
margin: 0;
padding-left: 0;
}
}
.agree {
background-color: rgba(255, 90, 0, 1);
border: 2.5px solid #cccccc;
box-shadow: inset 0 0 10px #666666;
color: white;
font-family: Cookbook, Georgia, 'Times New Roman', Times, serif;
font-size: 1em;
padding: 5px;
position: absolute;
bottom: 2.5px;
right: 2.5px;
text-shadow:
1px 0 0 black,
0 1px 0 black,
-1px 0 0 black,
0 -1px 0 black
;
transition: box-shadow 200ms;
&:disabled {
background-color: #333333;
border-color: #777777;
color: #777777;
}
&:not(:disabled):hover {
box-shadow: inset 0 0 10px #222222;
cursor: pointer;
text-decoration: underline;
}
}
.summary {
display: flex;
flex-direction: column;
gap: 2.5px;
width: 100%;
> li {
padding: 2.5px;
}
}
.gain {
background-color: rgba(0, 255, 0, 0.5);
border: 2.5px solid rgba(0, 255, 0, 0.5);
max-height: 40.5px;
overflow-y: auto;
}
.lose {
background-color: rgba(255, 0, 0, 0.5);
border: 2.5px solid rgba(255, 0, 0, 0.5);
max-height: 40.5px;
overflow-y: auto;
}
.items {
display: inline;
li {
display: inline;
&:not(:last-child):after {
content: ', ';
}
}
}
.make {
color: rgb(0, 255, 0);
}
.pay {
color: red;
}
.gold {
color: gold;
}
.qty {
font-family: Joystix;
font-size: 0.75em;
}
.cost {
position: absolute;
bottom: 2.5px;
left: 5px;
}
.amount {
font-size: 1.5em;
position: relative;
top: 2px;
}
.nop {
align-self: center;
font-size: 2em;
padding: 1em;
text-align: center;
width: 100%;
}

View File

@ -0,0 +1,17 @@
import styles from './wallet.module.css';
function Wallet({gold}) {
const goldString = gold.toString();
const pad = ''.padStart(7 - goldString.length, '0');
return (
<span className={styles.wallet}>
<span className={styles.number}>
<span className={styles.pad}>{pad}</span>
<span className={styles.amount}>{goldString}</span>
</span>
<span className={styles.gold}>🄶</span>
</span>
)
}
export default Wallet;

View File

@ -0,0 +1,19 @@
.wallet {
position: absolute;
right: 10px;
text-shadow: 1px 0 0 black, 0 1px 0 black, -1px 0 0 black, 0 -1px 0 black;
top: 20px;
}
.gold {
color: gold;
font-family: Cookbook, Georgia, 'Times New Roman', Times, serif;
}
.number {
font-family: Joystix;
}
.pad {
color: #555555;
}

View File

@ -15,6 +15,8 @@ import DateTime from './dom/datetime.jsx';
import Dom from './dom/dom.jsx'; import Dom from './dom/dom.jsx';
import Entities from './dom/entities.jsx'; import Entities from './dom/entities.jsx';
import External from './dom/external.jsx'; import External from './dom/external.jsx';
import Trade from './dom/trade.jsx';
import Wallet from './dom/wallet.jsx';
import HotBar from './dom/hotbar.jsx'; import HotBar from './dom/hotbar.jsx';
import Pixi from './pixi/pixi.jsx'; import Pixi from './pixi/pixi.jsx';
import Devtools from './devtools.jsx'; import Devtools from './devtools.jsx';
@ -62,7 +64,11 @@ function Ui({disconnected}) {
const [isInventoryOpen, setIsInventoryOpen] = useState(false); const [isInventoryOpen, setIsInventoryOpen] = useState(false);
const [externalInventory, setExternalInventory] = useState(); const [externalInventory, setExternalInventory] = useState();
const [externalInventorySlots, setExternalInventorySlots] = useState(); const [externalInventorySlots, setExternalInventorySlots] = useState();
const [gaining, setGaining] = useState([]);
const [losing, setLosing] = useState([]);
const [wallet, setWallet] = useState(0);
const [particleWorker, setParticleWorker] = useState(); const [particleWorker, setParticleWorker] = useState();
const [trading, setTrading] = useState(false);
useEffect(() => { useEffect(() => {
let handle; let handle;
if (disconnected) { if (disconnected) {
@ -245,6 +251,9 @@ function Ui({disconnected}) {
if (update.MainEntity) { if (update.MainEntity) {
mainEntityRef.current = id; mainEntityRef.current = id;
} }
if (update.Wallet && mainEntityRef.current === id) {
setWallet(update.Wallet.gold);
}
if (update.Inventory) { if (update.Inventory) {
if (mainEntityRef.current === id) { if (mainEntityRef.current === id) {
setBufferSlot(entity.Inventory.item(0)); setBufferSlot(entity.Inventory.item(0));
@ -267,6 +276,7 @@ function Ui({disconnected}) {
newInventorySlots[i] = entity.Inventory.item(i); newInventorySlots[i] = entity.Inventory.item(i);
} }
setExternalInventory(entity.id) setExternalInventory(entity.id)
setTrading(!!entity.Shop);
setExternalInventorySlots(newInventorySlots); setExternalInventorySlots(newInventorySlots);
setIsInventoryOpen(true); setIsInventoryOpen(true);
setHotbarIsHidden(false); setHotbarIsHidden(false);
@ -277,6 +287,9 @@ function Ui({disconnected}) {
else if (update.Inventory.closed) { else if (update.Inventory.closed) {
setExternalInventory(); setExternalInventory();
setExternalInventorySlots(); setExternalInventorySlots();
setGaining([]);
setLosing([]);
setTrading(false);
} }
} }
if (mainEntityRef.current === id) { if (mainEntityRef.current === id) {
@ -371,25 +384,67 @@ function Ui({disconnected}) {
mainEntityRef, mainEntityRef,
scale, scale,
]); ]);
const hotbarOnActivate = useCallback((i) => { const hotbarOnSlotMouseDown = useCallback((i) => {
keepHotbarOpen(); keepHotbarOpen();
if (trading) {
const index = losing.indexOf(i + 1);
if (-1 === index) {
losing.push(i + 1);
}
else {
losing.splice(index, 1);
}
setLosing([...losing]);
}
else {
client.send({ client.send({
type: 'Action', type: 'Action',
payload: {type: 'swapSlots', value: [0, mainEntityRef.current, i + 1]}, payload: {type: 'swapSlots', value: [0, mainEntityRef.current, i + 1]},
}); });
}, [client, keepHotbarOpen, mainEntityRef]); }
const bagOnActivate = useCallback((i) => { }, [client, keepHotbarOpen, losing, mainEntityRef, trading]);
const bagOnSlotMouseDown = useCallback((i) => {
if (trading) {
const index = losing.indexOf(i + 11);
if (-1 === index) {
losing.push(i + 11);
}
else {
losing.splice(index, 1);
}
setLosing([...losing]);
}
else {
client.send({ client.send({
type: 'Action', type: 'Action',
payload: {type: 'swapSlots', value: [0, mainEntityRef.current, i + 11]}, payload: {type: 'swapSlots', value: [0, mainEntityRef.current, i + 11]},
}); });
}, [client, mainEntityRef]); }
const externalInventoryOnActivate = useCallback((i) => { }, [client, losing, mainEntityRef, trading]);
const externalInventoryOnSlotMouseDown = useCallback((i) => {
if (trading) {
const index = gaining.indexOf(i);
if (-1 === index) {
gaining.push(i);
}
else {
gaining.splice(index, 1);
}
setGaining([...gaining]);
}
else {
client.send({ client.send({
type: 'Action', type: 'Action',
payload: {type: 'swapSlots', value: [0, externalInventory, i]}, payload: {type: 'swapSlots', value: [0, externalInventory, i]},
}); });
}, [client, externalInventory]); }
}, [client, externalInventory, gaining, trading]);
const onTradeAccepted = useCallback(() => {
client.send({
type: 'Action',
payload: {type: 'acceptTrade', value: {gaining, losing}},
});
}, [client, gaining, losing]);
useEffect(() => { useEffect(() => {
if (!pixiRef.current) { if (!pixiRef.current) {
return; return;
@ -510,21 +565,37 @@ function Ui({disconnected}) {
<Dom> <Dom>
<HotBar <HotBar
active={activeSlot} active={activeSlot}
highlighted={trading && losing.filter((i) => i < 11).map((i) => i - 1)}
hotbarIsHidden={hotbarIsHidden} hotbarIsHidden={hotbarIsHidden}
onActivate={hotbarOnActivate} onSlotMouseDown={hotbarOnSlotMouseDown}
slots={hotbarSlots} slots={hotbarSlots}
/> />
<Bag <Bag
highlighted={trading && losing.filter((i) => i >= 11).map((i) => i - 11)}
isInventoryOpen={isInventoryOpen} isInventoryOpen={isInventoryOpen}
onActivate={bagOnActivate} onSlotMouseDown={bagOnSlotMouseDown}
slots={inventorySlots} slots={inventorySlots}
/> />
{externalInventory && ( {externalInventory && (
<div className={styles.external}>
<External <External
highlighted={trading && gaining}
isInventoryOpen={isInventoryOpen} isInventoryOpen={isInventoryOpen}
onActivate={externalInventoryOnActivate} onSlotMouseDown={externalInventoryOnSlotMouseDown}
slots={externalInventorySlots} slots={externalInventorySlots}
/> />
{trading && (
<Trade
isInventoryOpen={isInventoryOpen}
onTradeAccepted={onTradeAccepted}
gaining={gaining.map((slot) => externalInventorySlots[slot])}
losing={losing.map((slot) => {
return slot < 11 ? hotbarSlots[slot - 1] : inventorySlots[slot - 11];
})}
wallet={wallet}
/>
)}
</div>
)} )}
<Entities <Entities
camera={camera} camera={camera}
@ -550,6 +621,7 @@ function Ui({disconnected}) {
<Disconnected /> <Disconnected />
)} )}
<DateTime /> <DateTime />
<Wallet gold={wallet} />
</Dom> </Dom>
</div> </div>
{devtoolsIsOpen && ( {devtoolsIsOpen && (

View File

@ -22,3 +22,13 @@
position: relative; position: relative;
user-select: none; user-select: none;
} }
.external {
display: flex;
left: 20px;
opacity: 1;
position: absolute;
top: 274px;
transition: top 150ms, opacity 200ms;
width: 100%;
}

View File

@ -152,6 +152,7 @@ export default async function createHomestead(id) {
}, },
}, },
Position: {x: 200, y: 200}, Position: {x: 200, y: 200},
Shop: {},
Sprite: { Sprite: {
anchorX: 0.5, anchorX: 0.5,
anchorY: 0.7, anchorY: 0.7,

View File

@ -49,6 +49,9 @@ export default async function createPlayer(id) {
}, },
Ticking: {}, Ticking: {},
VisibleAabb: {}, VisibleAabb: {},
Wallet: {
gold: 1000,
},
Wielder: { Wielder: {
activeSlot: 1, activeSlot: 1,
}, },

View File

@ -166,6 +166,8 @@ export default class Engine {
Interacts, Interacts,
Interlocutor, Interlocutor,
Inventory, Inventory,
Player,
Wallet,
Wielder, Wielder,
} = entity; } = entity;
const ecs = this.ecses[Ecs.path]; const ecs = this.ecses[Ecs.path];
@ -231,6 +233,33 @@ export default class Engine {
Controlled[payload.type] = payload.value; Controlled[payload.type] = payload.value;
break; break;
} }
case 'acceptTrade': {
const {losing, gaining} = payload.value;
const gainingSlots = gaining.filter((slot) => Player.openInventory.item(slot));
const losingSlots = losing.filter((slot) => Inventory.item(slot));
const nop = 0 === gainingSlots.length && 0 === losingSlots.length;
const gainingItems = gainingSlots.map((slot) => Player.openInventory.item(slot));
const losingItems = losingSlots.map((slot) => Inventory.item(slot));
if (nop) {
break;
}
const reducePrice = (r, item) => {
return r + item.price * item.qty;
};
const earn = losingItems.reduce(reducePrice, 0) - gainingItems.reduce(reducePrice, 0);
const canAfford = -earn <= Wallet.gold;
if (!canAfford) {
break;
}
for (const slot of losingSlots) {
Inventory.clear(slot);
}
for (const item of gainingItems) {
Inventory.give({qty: item.qty, source: item.source});
}
Wallet.gold += earn;
break;
}
case 'swapSlots': { case 'swapSlots': {
if (!Controlled.locked) { if (!Controlled.locked) {
const [l, other, r] = payload.value; const [l, other, r] = payload.value;

View File

@ -1,5 +1,6 @@
{ {
"icon": "/resources/brush/brush.png", "icon": "/resources/brush/brush.png",
"label": "Brush", "label": "Brush",
"price": 100,
"start": "/resources/brush/start.js" "start": "/resources/brush/start.js"
} }

View File

@ -1,4 +1,5 @@
{ {
"icon": "/resources/furball/furball.png", "icon": "/resources/furball/furball.png",
"label": "Fur Ball" "label": "Fur Ball",
"price": 5
} }

View File

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

View File

@ -1,5 +1,6 @@
{ {
"icon": "/resources/magic-swords/icon.png", "icon": "/resources/magic-swords/icon.png",
"label": "Magic swords", "label": "Magic swords",
"price": 2000,
"start": "/resources/magic-swords/start.js" "start": "/resources/magic-swords/start.js"
} }

View File

@ -1,5 +1,6 @@
{ {
"icon": "/resources/potion/icon.png", "icon": "/resources/potion/icon.png",
"label": "Potion", "label": "Potion",
"price": 50,
"start": "/resources/potion/start.js" "start": "/resources/potion/start.js"
} }

View File

@ -1,6 +1,7 @@
{ {
"icon": "/resources/tomato-seeds/icon.png", "icon": "/resources/tomato-seeds/icon.png",
"label": "Tomato Seeds", "label": "Tomato Seeds",
"price": 20,
"projection": { "projection": {
"distance": [1, -1], "distance": [1, -1],
"grid": [ "grid": [

View File

@ -1,3 +1,5 @@
{ {
"icon": "/resources/tomato/tomato.png" "icon": "/resources/tomato/tomato.png",
"label": "Tomato",
"price": 20
} }

View File

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

View File

@ -14,11 +14,11 @@ export default {
decorators: [ decorators: [
(Hotbar, ctx) => { (Hotbar, ctx) => {
const [, updateArgs] = useArgs(); const [, updateArgs] = useArgs();
const {onActivate} = ctx.args; const {onSlotMouseDown} = ctx.args;
ctx.args.onActivate = (i) => { ctx.args.onSlotMouseDown = (i) => {
updateArgs({active: i}); updateArgs({active: i});
if (onActivate) { if (onSlotMouseDown) {
onActivate(i); onSlotMouseDown(i);
} }
}; };
return Hotbar(); return Hotbar();
@ -33,7 +33,7 @@ export default {
tags: ['autodocs'], tags: ['autodocs'],
args: { args: {
active: 0, active: 0,
onActivate: fn(), onSlotMouseDown: fn(),
slots, slots,
}, },
argTypes: { argTypes: {