@rpg-engine/long-bow 0.7.89 → 0.7.91

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpg-engine/long-bow",
3
- "version": "0.7.89",
3
+ "version": "0.7.91",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -13,13 +13,14 @@ import { DraggableContainer } from '../DraggableContainer';
13
13
  import { InputRadio } from '../InputRadio';
14
14
  import { RPGUIContainerTypes } from '../RPGUI/RPGUIContainer';
15
15
  import { CraftingRecipe } from './CraftingRecipe';
16
+ import { calculateMaxCraftable } from './utils/calculateMaxCraftable';
16
17
 
17
18
  export interface IItemCraftSelectorProps {
18
19
  atlasJSON: any;
19
20
  atlasIMG: any;
20
21
  onClose: () => void;
21
22
  onSelect: (value: string) => void;
22
- onCraftItem: (value: string | undefined) => void;
23
+ onCraftItem: (value: string | undefined, maxCraftable: number) => void;
23
24
  craftablesItems: ICraftableItem[];
24
25
  equipmentSet?: IEquipmentSet | null;
25
26
  inventory?: IItemContainer | null;
@@ -66,7 +67,7 @@ export const CraftBook: React.FC<IItemCraftSelectorProps> = ({
66
67
  savedSelectedType ?? Object.keys(ItemSubType)[0]
67
68
  );
68
69
  const [size, setSize] = useState<{ width: string; height: string }>();
69
-
70
+ const [searchTerm, setSearchTerm] = useState('');
70
71
  const [isCraftingDisabled, setIsCraftingDisabled] = useState(false);
71
72
 
72
73
  useEffect(() => {
@@ -149,6 +150,15 @@ export const CraftBook: React.FC<IItemCraftSelectorProps> = ({
149
150
  ));
150
151
  };
151
152
 
153
+ const filteredCraftableItems = craftablesItems?.filter(item => {
154
+ const matchesSearch = item.name
155
+ .toLowerCase()
156
+ .includes(searchTerm.toLowerCase());
157
+ const matchesCategory =
158
+ selectedType === 'Suggested' || item.type === selectedType;
159
+ return matchesSearch && matchesCategory;
160
+ });
161
+
152
162
  if (!size) return null;
153
163
 
154
164
  return (
@@ -168,6 +178,15 @@ export const CraftBook: React.FC<IItemCraftSelectorProps> = ({
168
178
  <div style={{ width: '100%' }}>
169
179
  <Title>Craftbook</Title>
170
180
  <Subtitle>Select an item to craft</Subtitle>
181
+ <SearchContainer>
182
+ <input
183
+ type="text"
184
+ className="rpgui-input"
185
+ placeholder="Search items..."
186
+ value={searchTerm}
187
+ onChange={e => setSearchTerm(e.target.value)}
188
+ />
189
+ </SearchContainer>
171
190
  <hr className="golden" />
172
191
  </div>
173
192
 
@@ -177,7 +196,7 @@ export const CraftBook: React.FC<IItemCraftSelectorProps> = ({
177
196
  </ItemTypes>
178
197
 
179
198
  <RadioInputScroller className="inputRadioCraftBook">
180
- {craftablesItems?.map(item => (
199
+ {filteredCraftableItems?.map(item => (
181
200
  <CraftingRecipe
182
201
  key={item.key}
183
202
  atlasIMG={atlasIMG}
@@ -203,7 +222,15 @@ export const CraftBook: React.FC<IItemCraftSelectorProps> = ({
203
222
  onPointerDown={() => {
204
223
  if (!craftItemKey || isCraftingDisabled) return;
205
224
 
206
- onCraftItem(craftItemKey);
225
+ const selectedItem = craftablesItems.find(
226
+ item => item.key === craftItemKey
227
+ );
228
+ const maxCraftable = calculateMaxCraftable(
229
+ selectedItem,
230
+ inventory
231
+ );
232
+
233
+ onCraftItem(craftItemKey, maxCraftable);
207
234
  setIsCraftingDisabled(true);
208
235
 
209
236
  setTimeout(() => {
@@ -295,3 +322,27 @@ const ItemTypes = styled.div`
295
322
  width: 100%;
296
323
  }
297
324
  `;
325
+
326
+ const SearchContainer = styled.div`
327
+ margin: 8px 0;
328
+ padding: 0 16px;
329
+
330
+ input {
331
+ width: 100%;
332
+ font-size: 0.8rem;
333
+ padding: 8px 12px;
334
+ background-color: rgba(0, 0, 0, 0.3);
335
+ border: none;
336
+ color: white;
337
+ border-radius: 4px;
338
+
339
+ &::placeholder {
340
+ color: rgba(255, 255, 255, 0.5);
341
+ }
342
+
343
+ &:focus {
344
+ outline: none;
345
+ background-color: rgba(0, 0, 0, 0.4);
346
+ }
347
+ }
348
+ `;
@@ -0,0 +1,30 @@
1
+ import { ICraftableItem, IItem, IItemContainer } from '@rpg-engine/shared';
2
+
3
+ export function calculateMaxCraftable(
4
+ recipe: ICraftableItem | undefined,
5
+ inventory: IItemContainer | null | undefined
6
+ ): number {
7
+ if (!inventory || !recipe?.ingredients) {
8
+ return 0;
9
+ }
10
+
11
+ return recipe.ingredients.reduce((maxPossible, ingredient) => {
12
+ const inventoryItem = Object.values(inventory.slots).find(
13
+ (item): item is IItem => item?.key === ingredient.key
14
+ );
15
+
16
+ if (!inventoryItem) {
17
+ return 0;
18
+ }
19
+
20
+ const possibleWithThisIngredient = Math.floor(
21
+ inventoryItem.stackQty ?? 0 / ingredient.qty
22
+ );
23
+
24
+ if (maxPossible === -1) {
25
+ return possibleWithThisIngredient;
26
+ }
27
+
28
+ return Math.min(maxPossible, possibleWithThisIngredient);
29
+ }, -1);
30
+ }
@@ -22,7 +22,12 @@ const Template: Story = args => (
22
22
  onSelect={onSelect}
23
23
  onClose={() => console.log('closing Equipment Set Container')}
24
24
  craftablesItems={craftableItems}
25
- onCraftItem={onCraftItem}
25
+ onCraftItem={(value, maxCraftable) => {
26
+ console.log('Crafting:', {
27
+ itemKey: value,
28
+ maxCraftable: maxCraftable,
29
+ });
30
+ }}
26
31
  equipmentSet={equipmentSetMock}
27
32
  {...args}
28
33
  />
@@ -31,10 +36,6 @@ const Template: Story = args => (
31
36
 
32
37
  export const Default = Template.bind({});
33
38
 
34
- const onCraftItem = (value: string | undefined) => {
35
- console.log('Craft', value);
36
- };
37
-
38
39
  const onSelect = (value: string) => {
39
40
  console.log('Story package', value);
40
41
  };