@rpg-engine/long-bow 0.8.20 → 0.8.22
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/dist/components/InformationCenter/InformationCenter.d.ts +5 -15
- package/dist/components/InformationCenter/sections/bestiary/InformationCenterBestiarySection.d.ts +3 -1
- package/dist/components/InformationCenter/sections/bestiary/InformationCenterNPCDetails.d.ts +4 -1
- package/dist/components/InformationCenter/sections/bestiary/InformationCenterNPCTooltip.d.ts +1 -1
- package/dist/components/InformationCenter/sections/faq/InformationCenterFaqSection.d.ts +1 -1
- package/dist/components/InformationCenter/sections/items/InformationCenterItemDetails.d.ts +1 -1
- package/dist/components/InformationCenter/sections/items/InformationCenterItemTooltip.d.ts +1 -1
- package/dist/components/InformationCenter/sections/items/InformationCenterItemsSection.d.ts +2 -1
- package/dist/components/InformationCenter/sections/tutorials/InformationCenterTutorialsSection.d.ts +1 -1
- package/dist/long-bow.cjs.development.js +793 -660
- package/dist/long-bow.cjs.development.js.map +1 -1
- package/dist/long-bow.cjs.production.min.js +1 -1
- package/dist/long-bow.cjs.production.min.js.map +1 -1
- package/dist/long-bow.esm.js +794 -661
- package/dist/long-bow.esm.js.map +1 -1
- package/dist/mocks/informationCenter.mocks.d.ts +1 -2
- package/package.json +2 -2
- package/src/components/CraftBook/CraftBook.tsx +1 -1
- package/src/components/Dropdown.tsx +25 -3
- package/src/components/InformationCenter/InformationCenter.tsx +11 -18
- package/src/components/InformationCenter/sections/bestiary/InformationCenterBestiarySection.tsx +58 -7
- package/src/components/InformationCenter/sections/bestiary/InformationCenterNPCDetails.tsx +63 -15
- package/src/components/InformationCenter/sections/bestiary/InformationCenterNPCTooltip.tsx +7 -6
- package/src/components/InformationCenter/sections/faq/InformationCenterFaqSection.tsx +1 -1
- package/src/components/InformationCenter/sections/items/InformationCenterItemDetails.tsx +8 -6
- package/src/components/InformationCenter/sections/items/InformationCenterItemTooltip.tsx +1 -1
- package/src/components/InformationCenter/sections/items/InformationCenterItemsSection.tsx +36 -10
- package/src/components/InformationCenter/sections/tutorials/InformationCenterTutorialsSection.tsx +133 -34
- package/src/components/shared/PaginatedContent/PaginatedContent.tsx +1 -1
- package/src/mocks/informationCenter.mocks.ts +175 -55
- package/src/stories/UI/info/InformationCenter.stories.tsx +6 -1
- package/dist/components/InformationCenter/InformationCenterTypes.d.ts +0 -79
- package/src/components/InformationCenter/InformationCenterTypes.ts +0 -87
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import { IFaqItem, IVideoGuide } from '
|
|
2
|
-
import { IInformationCenterItem, IInformationCenterNPC } from '../components/InformationCenter/InformationCenterTypes';
|
|
1
|
+
import { IFaqItem, IInformationCenterItem, IInformationCenterNPC, IVideoGuide } from '@rpg-engine/shared';
|
|
3
2
|
export declare const mockBestiaryItems: IInformationCenterNPC[];
|
|
4
3
|
export declare const mockItems: IInformationCenterItem[];
|
|
5
4
|
export declare const mockFaqItems: IFaqItem[];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rpg-engine/long-bow",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.22",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"typings": "dist/index.d.ts",
|
|
@@ -84,7 +84,7 @@
|
|
|
84
84
|
"dependencies": {
|
|
85
85
|
"@capacitor/core": "^6.1.0",
|
|
86
86
|
"@rollup/plugin-image": "^2.1.1",
|
|
87
|
-
"@rpg-engine/shared": "^0.9.
|
|
87
|
+
"@rpg-engine/shared": "^0.9.101",
|
|
88
88
|
"dayjs": "^1.11.2",
|
|
89
89
|
"font-awesome": "^4.7.0",
|
|
90
90
|
"fs-extra": "^10.1.0",
|
|
@@ -15,6 +15,10 @@ export interface IDropdownProps {
|
|
|
15
15
|
opensUp?: boolean; // Add a new prop to control the dropdown direction
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
const ITEM_HEIGHT = 35;
|
|
19
|
+
const MAX_ITEMS_VISIBLE = 10;
|
|
20
|
+
const MAX_HEIGHT = MAX_ITEMS_VISIBLE * ITEM_HEIGHT;
|
|
21
|
+
|
|
18
22
|
export const Dropdown: React.FC<IDropdownProps> = ({
|
|
19
23
|
options,
|
|
20
24
|
width,
|
|
@@ -65,6 +69,7 @@ export const Dropdown: React.FC<IDropdownProps> = ({
|
|
|
65
69
|
className="rpgui-dropdown-imp"
|
|
66
70
|
opened={opened}
|
|
67
71
|
opensUp={opensUp}
|
|
72
|
+
optionsCount={options.length}
|
|
68
73
|
>
|
|
69
74
|
{options.map(option => {
|
|
70
75
|
return (
|
|
@@ -88,11 +93,16 @@ export const Dropdown: React.FC<IDropdownProps> = ({
|
|
|
88
93
|
const Container = styled.div<{ width: string | undefined }>`
|
|
89
94
|
position: relative;
|
|
90
95
|
width: ${props => props.width || '100%'};
|
|
96
|
+
max-height: ${MAX_HEIGHT}px;
|
|
91
97
|
`;
|
|
92
98
|
|
|
93
99
|
const DropdownSelect = styled.p`
|
|
94
100
|
width: 100%;
|
|
95
101
|
box-sizing: border-box;
|
|
102
|
+
white-space: nowrap;
|
|
103
|
+
overflow: hidden;
|
|
104
|
+
text-overflow: ellipsis;
|
|
105
|
+
max-height: ${ITEM_HEIGHT}px;
|
|
96
106
|
|
|
97
107
|
label {
|
|
98
108
|
display: inline-block;
|
|
@@ -103,10 +113,14 @@ const DropdownSelect = styled.p`
|
|
|
103
113
|
const DropdownOptions = styled.ul<{
|
|
104
114
|
opened: boolean;
|
|
105
115
|
opensUp: boolean;
|
|
116
|
+
optionsCount: number;
|
|
106
117
|
}>`
|
|
107
118
|
position: absolute;
|
|
108
119
|
width: 100%;
|
|
109
|
-
max-height:
|
|
120
|
+
max-height: ${props =>
|
|
121
|
+
props.opened
|
|
122
|
+
? `${Math.min(props.optionsCount * ITEM_HEIGHT, MAX_HEIGHT)}px`
|
|
123
|
+
: '0'};
|
|
110
124
|
overflow-y: auto;
|
|
111
125
|
display: ${props => (props.opened ? 'block' : 'none')};
|
|
112
126
|
box-sizing: border-box;
|
|
@@ -114,7 +128,15 @@ const DropdownOptions = styled.ul<{
|
|
|
114
128
|
top: ${props => (props.opensUp ? 'auto' : '100%')};
|
|
115
129
|
margin: 0;
|
|
116
130
|
padding: 0;
|
|
117
|
-
|
|
118
|
-
|
|
131
|
+
|
|
132
|
+
li {
|
|
133
|
+
white-space: nowrap;
|
|
134
|
+
overflow: hidden;
|
|
135
|
+
text-overflow: ellipsis;
|
|
136
|
+
padding: 8px 12px;
|
|
137
|
+
min-height: ${ITEM_HEIGHT}px;
|
|
138
|
+
max-height: ${ITEM_HEIGHT}px;
|
|
139
|
+
box-sizing: border-box;
|
|
140
|
+
line-height: 1.2;
|
|
119
141
|
}
|
|
120
142
|
`;
|
|
@@ -1,37 +1,26 @@
|
|
|
1
1
|
import React, { useState } from 'react';
|
|
2
2
|
import styled from 'styled-components';
|
|
3
3
|
|
|
4
|
-
import { InternalTabs } from '../InternalTabs/InternalTabs';
|
|
5
4
|
import {
|
|
5
|
+
IFaqItem,
|
|
6
6
|
IInformationCenterItem,
|
|
7
7
|
IInformationCenterNPC,
|
|
8
|
-
|
|
8
|
+
IVideoGuide,
|
|
9
|
+
} from '@rpg-engine/shared';
|
|
10
|
+
import { InternalTabs } from '../InternalTabs/InternalTabs';
|
|
9
11
|
import { InformationCenterBestiarySection } from './sections/bestiary/InformationCenterBestiarySection';
|
|
10
12
|
import { InformationCenterFAQSection } from './sections/faq/InformationCenterFaqSection';
|
|
11
13
|
import { InformationCenterItemDetails } from './sections/items/InformationCenterItemDetails';
|
|
12
14
|
import { InformationCenterItemsSection } from './sections/items/InformationCenterItemsSection';
|
|
13
15
|
import { InformationCenterTutorialsSection } from './sections/tutorials/InformationCenterTutorialsSection';
|
|
14
16
|
|
|
15
|
-
|
|
16
|
-
id: string;
|
|
17
|
-
question: string;
|
|
18
|
-
answer: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface IVideoGuide {
|
|
22
|
-
id: string;
|
|
23
|
-
title: string;
|
|
24
|
-
description: string;
|
|
25
|
-
thumbnailUrl: string;
|
|
26
|
-
videoUrl: string;
|
|
27
|
-
category: 'Combat' | 'Crafting' | 'Exploration' | 'General';
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
export interface IInformationCenterProps {
|
|
17
|
+
interface IInformationCenterProps {
|
|
31
18
|
itemsAtlasJSON: Record<string, any>;
|
|
32
19
|
itemsAtlasIMG: string;
|
|
33
20
|
entitiesAtlasJSON: Record<string, any>;
|
|
34
21
|
entitiesAtlasIMG: string;
|
|
22
|
+
iconsAtlasJSON: any;
|
|
23
|
+
iconsAtlasIMG: any;
|
|
35
24
|
faqItems?: IFaqItem[];
|
|
36
25
|
bestiaryItems?: IInformationCenterNPC[];
|
|
37
26
|
videoGuides?: IVideoGuide[];
|
|
@@ -46,6 +35,8 @@ export const InformationCenter: React.FC<IInformationCenterProps> = ({
|
|
|
46
35
|
itemsAtlasIMG,
|
|
47
36
|
entitiesAtlasJSON,
|
|
48
37
|
entitiesAtlasIMG,
|
|
38
|
+
iconsAtlasIMG,
|
|
39
|
+
iconsAtlasJSON,
|
|
49
40
|
faqItems = [],
|
|
50
41
|
bestiaryItems = [],
|
|
51
42
|
videoGuides = [],
|
|
@@ -77,6 +68,8 @@ export const InformationCenter: React.FC<IInformationCenterProps> = ({
|
|
|
77
68
|
bestiaryItems={bestiaryItems}
|
|
78
69
|
itemsAtlasJSON={itemsAtlasJSON}
|
|
79
70
|
itemsAtlasIMG={itemsAtlasIMG}
|
|
71
|
+
iconsAtlasIMG={iconsAtlasIMG}
|
|
72
|
+
iconsAtlasJSON={iconsAtlasJSON}
|
|
80
73
|
entitiesAtlasJSON={entitiesAtlasJSON}
|
|
81
74
|
entitiesAtlasIMG={entitiesAtlasIMG}
|
|
82
75
|
initialSearchQuery={initialSearchQuery}
|
package/src/components/InformationCenter/sections/bestiary/InformationCenterBestiarySection.tsx
CHANGED
|
@@ -1,18 +1,21 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { IInformationCenterNPC, NPCAlignment } from '@rpg-engine/shared';
|
|
2
|
+
import React, { useMemo, useState } from 'react';
|
|
2
3
|
import styled from 'styled-components';
|
|
4
|
+
import { IOptionsProps } from '../../../Dropdown';
|
|
3
5
|
import { PaginatedContent } from '../../../shared/PaginatedContent/PaginatedContent';
|
|
4
6
|
import { Portal } from '../../../shared/Portal/Portal';
|
|
5
7
|
import { InformationCenterCell } from '../../InformationCenterCell';
|
|
6
|
-
import {
|
|
8
|
+
import { formatItemType } from '../items/InformationCenterItemsSection';
|
|
7
9
|
import { InformationCenterNPCDetails } from './InformationCenterNPCDetails';
|
|
8
10
|
import { InformationCenterNPCTooltip } from './InformationCenterNPCTooltip';
|
|
9
|
-
|
|
10
11
|
interface IBestiarySectionProps {
|
|
11
12
|
bestiaryItems: IInformationCenterNPC[];
|
|
12
13
|
itemsAtlasJSON: Record<string, any>;
|
|
13
14
|
itemsAtlasIMG: string;
|
|
14
15
|
entitiesAtlasJSON: Record<string, any>;
|
|
15
16
|
entitiesAtlasIMG: string;
|
|
17
|
+
iconsAtlasIMG: any;
|
|
18
|
+
iconsAtlasJSON: any;
|
|
16
19
|
initialSearchQuery: string;
|
|
17
20
|
tabId: string;
|
|
18
21
|
}
|
|
@@ -21,6 +24,8 @@ export const InformationCenterBestiarySection: React.FC<IBestiarySectionProps> =
|
|
|
21
24
|
bestiaryItems,
|
|
22
25
|
itemsAtlasJSON,
|
|
23
26
|
itemsAtlasIMG,
|
|
27
|
+
iconsAtlasIMG,
|
|
28
|
+
iconsAtlasJSON,
|
|
24
29
|
entitiesAtlasJSON,
|
|
25
30
|
entitiesAtlasIMG,
|
|
26
31
|
initialSearchQuery,
|
|
@@ -87,6 +92,20 @@ export const InformationCenterBestiarySection: React.FC<IBestiarySectionProps> =
|
|
|
87
92
|
setTooltipData(null);
|
|
88
93
|
};
|
|
89
94
|
|
|
95
|
+
const [selectedBestiaryCategory, setSelectedBestiaryCategory] = useState<
|
|
96
|
+
string
|
|
97
|
+
>('all');
|
|
98
|
+
|
|
99
|
+
const bestiaryCategoryOptions: IOptionsProps[] = [
|
|
100
|
+
{ id: 0, value: 'all', option: 'All Monsters' },
|
|
101
|
+
{ id: 1, value: 'bosses', option: 'Bosses' },
|
|
102
|
+
...Object.entries(NPCAlignment).map(([, value], index) => ({
|
|
103
|
+
id: index + 1,
|
|
104
|
+
value,
|
|
105
|
+
option: formatItemType(value),
|
|
106
|
+
})),
|
|
107
|
+
];
|
|
108
|
+
|
|
90
109
|
const renderItem = (item: IInformationCenterNPC) => (
|
|
91
110
|
<InformationCenterCell
|
|
92
111
|
key={item.id}
|
|
@@ -102,9 +121,33 @@ export const InformationCenterBestiarySection: React.FC<IBestiarySectionProps> =
|
|
|
102
121
|
/>
|
|
103
122
|
);
|
|
104
123
|
|
|
105
|
-
const filteredItems =
|
|
106
|
-
|
|
107
|
-
|
|
124
|
+
const filteredItems = useMemo(() => {
|
|
125
|
+
return bestiaryItems.filter(item => {
|
|
126
|
+
const matchesSearch = item.name
|
|
127
|
+
.toLowerCase()
|
|
128
|
+
.includes(searchQuery.toLowerCase());
|
|
129
|
+
|
|
130
|
+
let matchesCategory = true;
|
|
131
|
+
|
|
132
|
+
if (selectedBestiaryCategory === 'bosses') {
|
|
133
|
+
matchesCategory = item.isBoss === true;
|
|
134
|
+
} else if (selectedBestiaryCategory === NPCAlignment.Hostile) {
|
|
135
|
+
matchesCategory =
|
|
136
|
+
item.alignment === NPCAlignment.Hostile && !item.isBoss;
|
|
137
|
+
} else if (selectedBestiaryCategory !== 'all') {
|
|
138
|
+
matchesCategory = item.alignment === selectedBestiaryCategory;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return matchesSearch && matchesCategory;
|
|
142
|
+
});
|
|
143
|
+
}, [bestiaryItems, searchQuery, selectedBestiaryCategory]);
|
|
144
|
+
|
|
145
|
+
const handleSearchChange = (newQuery: string) => {
|
|
146
|
+
setSearchQuery(newQuery);
|
|
147
|
+
if (newQuery && selectedBestiaryCategory !== 'all') {
|
|
148
|
+
setSelectedBestiaryCategory('all');
|
|
149
|
+
}
|
|
150
|
+
};
|
|
108
151
|
|
|
109
152
|
return (
|
|
110
153
|
<>
|
|
@@ -114,11 +157,17 @@ export const InformationCenterBestiarySection: React.FC<IBestiarySectionProps> =
|
|
|
114
157
|
emptyMessage="No monsters found"
|
|
115
158
|
tabId={tabId}
|
|
116
159
|
layout="grid"
|
|
160
|
+
filterOptions={{
|
|
161
|
+
options: bestiaryCategoryOptions,
|
|
162
|
+
selectedOption: selectedBestiaryCategory,
|
|
163
|
+
onOptionChange: setSelectedBestiaryCategory,
|
|
164
|
+
}}
|
|
117
165
|
searchOptions={{
|
|
118
166
|
value: searchQuery,
|
|
119
|
-
onChange:
|
|
167
|
+
onChange: handleSearchChange,
|
|
120
168
|
placeholder: 'Search monsters...',
|
|
121
169
|
}}
|
|
170
|
+
dependencies={[selectedBestiaryCategory]}
|
|
122
171
|
itemHeight="180px"
|
|
123
172
|
/>
|
|
124
173
|
{tooltipData && (
|
|
@@ -143,6 +192,8 @@ export const InformationCenterBestiarySection: React.FC<IBestiarySectionProps> =
|
|
|
143
192
|
npc={selectedMonster}
|
|
144
193
|
itemsAtlasJSON={itemsAtlasJSON}
|
|
145
194
|
itemsAtlasIMG={itemsAtlasIMG}
|
|
195
|
+
iconAtlasIMG={iconsAtlasIMG}
|
|
196
|
+
iconAtlasJSON={iconsAtlasJSON}
|
|
146
197
|
entitiesAtlasJSON={entitiesAtlasJSON}
|
|
147
198
|
entitiesAtlasIMG={entitiesAtlasIMG}
|
|
148
199
|
onBack={() => setSelectedMonster(null)}
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
IInformationCenterNPC,
|
|
3
|
+
isMobileOrTablet,
|
|
4
|
+
MovementSpeed,
|
|
5
|
+
RangeTypes,
|
|
6
|
+
} from '@rpg-engine/shared';
|
|
2
7
|
import React, { useState } from 'react';
|
|
3
8
|
import styled from 'styled-components';
|
|
4
9
|
import { uiColors } from '../../../../constants/uiColors';
|
|
@@ -6,10 +11,7 @@ import { Collapsible } from '../../../shared/Collapsible/Collapsible';
|
|
|
6
11
|
import { Pagination } from '../../../shared/Pagination/Pagination';
|
|
7
12
|
import { SearchBar } from '../../../shared/SearchBar/SearchBar';
|
|
8
13
|
import { SpriteFromAtlas } from '../../../shared/SpriteFromAtlas';
|
|
9
|
-
|
|
10
|
-
IInformationCenterNPC,
|
|
11
|
-
MovementSpeed,
|
|
12
|
-
} from '../../InformationCenterTypes';
|
|
14
|
+
|
|
13
15
|
import { BaseInformationDetails } from '../../shared/BaseInformationDetails';
|
|
14
16
|
|
|
15
17
|
interface INPCDetailsProps {
|
|
@@ -18,17 +20,35 @@ interface INPCDetailsProps {
|
|
|
18
20
|
itemsAtlasIMG: string;
|
|
19
21
|
entitiesAtlasJSON: Record<string, any>;
|
|
20
22
|
entitiesAtlasIMG: string;
|
|
23
|
+
iconAtlasIMG?: any;
|
|
24
|
+
iconAtlasJSON?: any;
|
|
21
25
|
onBack: () => void;
|
|
22
26
|
}
|
|
23
27
|
|
|
24
28
|
const ITEMS_PER_PAGE = 5;
|
|
25
29
|
|
|
30
|
+
export const formatItemName = (itemPath: string): string => {
|
|
31
|
+
const cleanText =
|
|
32
|
+
itemPath
|
|
33
|
+
.split('/')
|
|
34
|
+
.pop()
|
|
35
|
+
?.split('.')
|
|
36
|
+
.shift() || '';
|
|
37
|
+
|
|
38
|
+
return cleanText
|
|
39
|
+
.split('-')
|
|
40
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
41
|
+
.join(' ');
|
|
42
|
+
};
|
|
43
|
+
|
|
26
44
|
export const InformationCenterNPCDetails: React.FC<INPCDetailsProps> = ({
|
|
27
45
|
npc,
|
|
28
46
|
itemsAtlasJSON,
|
|
29
47
|
itemsAtlasIMG,
|
|
30
48
|
entitiesAtlasJSON,
|
|
31
49
|
entitiesAtlasIMG,
|
|
50
|
+
iconAtlasIMG,
|
|
51
|
+
iconAtlasJSON,
|
|
32
52
|
onBack,
|
|
33
53
|
}) => {
|
|
34
54
|
const isMobile = isMobileOrTablet();
|
|
@@ -39,12 +59,14 @@ export const InformationCenterNPCDetails: React.FC<INPCDetailsProps> = ({
|
|
|
39
59
|
if (typeof text === 'number') {
|
|
40
60
|
return text.toString();
|
|
41
61
|
}
|
|
62
|
+
|
|
42
63
|
return text
|
|
43
64
|
.toString()
|
|
44
65
|
.replace(/([A-Z])/g, ' $1')
|
|
45
66
|
.trim()
|
|
46
67
|
.replace(/([A-Z]+)([A-Z][a-z])/g, '$1 $2')
|
|
47
|
-
.replace(/\s+/g, ' ')
|
|
68
|
+
.replace(/\s+/g, ' ')
|
|
69
|
+
.replace(/^\w/, c => c.toUpperCase());
|
|
48
70
|
};
|
|
49
71
|
|
|
50
72
|
const formatRarity = (rarity: number): string => {
|
|
@@ -68,7 +90,7 @@ export const InformationCenterNPCDetails: React.FC<INPCDetailsProps> = ({
|
|
|
68
90
|
|
|
69
91
|
const filteredLoots =
|
|
70
92
|
npc.loots?.filter(loot =>
|
|
71
|
-
|
|
93
|
+
formatItemName(loot.itemBlueprintKey)
|
|
72
94
|
.toLowerCase()
|
|
73
95
|
.includes(lootSearchQuery.toLowerCase())
|
|
74
96
|
) || [];
|
|
@@ -147,7 +169,7 @@ export const InformationCenterNPCDetails: React.FC<INPCDetailsProps> = ({
|
|
|
147
169
|
imgScale={1}
|
|
148
170
|
/>
|
|
149
171
|
<LootDetails>
|
|
150
|
-
<LootName>{
|
|
172
|
+
<LootName>{formatItemName(loot.itemBlueprintKey)}</LootName>
|
|
151
173
|
<LootInfo>
|
|
152
174
|
<LootChance>{formatRarity(loot.chance)}</LootChance>
|
|
153
175
|
{loot.quantityRange && (
|
|
@@ -187,12 +209,24 @@ export const InformationCenterNPCDetails: React.FC<INPCDetailsProps> = ({
|
|
|
187
209
|
<SpellsList>
|
|
188
210
|
{npc.areaSpells.map((spell, index) => (
|
|
189
211
|
<SpellItem key={index}>
|
|
190
|
-
<
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
212
|
+
<SpellIconContainer>
|
|
213
|
+
<SpriteFromAtlas
|
|
214
|
+
atlasJSON={iconAtlasJSON}
|
|
215
|
+
atlasIMG={iconAtlasIMG}
|
|
216
|
+
spriteKey={spell.texturePath || spell.spellKey}
|
|
217
|
+
width={24}
|
|
218
|
+
height={24}
|
|
219
|
+
imgScale={1}
|
|
220
|
+
/>
|
|
221
|
+
</SpellIconContainer>
|
|
222
|
+
<SpellContent>
|
|
223
|
+
<SpellName>{formatText(spell.spellKey)}</SpellName>
|
|
224
|
+
<SpellDetails>
|
|
225
|
+
Power: <SpellValue>{formatText(spell.power)}</SpellValue>
|
|
226
|
+
<Separator>•</Separator>
|
|
227
|
+
Chance: <SpellValue>{spell.probability}%</SpellValue>
|
|
228
|
+
</SpellDetails>
|
|
229
|
+
</SpellContent>
|
|
196
230
|
</SpellItem>
|
|
197
231
|
))}
|
|
198
232
|
</SpellsList>
|
|
@@ -271,13 +305,27 @@ const SpellsList = styled.div`
|
|
|
271
305
|
padding: 12px;
|
|
272
306
|
`;
|
|
273
307
|
|
|
274
|
-
const
|
|
308
|
+
const SpellIconContainer = styled.div`
|
|
309
|
+
display: flex;
|
|
310
|
+
padding-right: 30px;
|
|
311
|
+
padding-bottom: 30px;
|
|
312
|
+
`;
|
|
313
|
+
|
|
314
|
+
const SpellContent = styled.div`
|
|
275
315
|
display: flex;
|
|
276
316
|
flex-direction: column;
|
|
277
317
|
gap: 4px;
|
|
318
|
+
flex: 1;
|
|
319
|
+
`;
|
|
320
|
+
|
|
321
|
+
const SpellItem = styled.div`
|
|
322
|
+
display: flex;
|
|
323
|
+
gap: 8px;
|
|
278
324
|
background: rgba(255, 255, 255, 0.05);
|
|
279
325
|
padding: 8px;
|
|
326
|
+
padding-left: 10px;
|
|
280
327
|
border-radius: 4px;
|
|
328
|
+
align-items: center;
|
|
281
329
|
`;
|
|
282
330
|
|
|
283
331
|
const SpellName = styled.div`
|
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
IInformationCenterNPC,
|
|
3
|
+
MovementSpeed,
|
|
4
|
+
RangeTypes,
|
|
5
|
+
} from '@rpg-engine/shared';
|
|
2
6
|
import React from 'react';
|
|
3
7
|
import styled from 'styled-components';
|
|
4
8
|
import { uiColors } from '../../../../constants/uiColors';
|
|
@@ -10,10 +14,7 @@ import BaseTooltip, {
|
|
|
10
14
|
TooltipTitle,
|
|
11
15
|
} from '../../../shared/BaseTooltip';
|
|
12
16
|
import { SpriteFromAtlas } from '../../../shared/SpriteFromAtlas';
|
|
13
|
-
import {
|
|
14
|
-
IInformationCenterNPC,
|
|
15
|
-
MovementSpeed,
|
|
16
|
-
} from '../../InformationCenterTypes';
|
|
17
|
+
import { formatItemName } from './InformationCenterNPCDetails';
|
|
17
18
|
|
|
18
19
|
interface INPCTooltipProps {
|
|
19
20
|
npc: IInformationCenterNPC;
|
|
@@ -191,7 +192,7 @@ export const InformationCenterNPCTooltip: React.FC<INPCTooltipProps> = ({
|
|
|
191
192
|
spriteKey={loot.itemBlueprintKey}
|
|
192
193
|
imgScale={1}
|
|
193
194
|
/>
|
|
194
|
-
<LootName>{
|
|
195
|
+
<LootName>{formatItemName(loot.itemBlueprintKey)}</LootName>
|
|
195
196
|
<LootChance>{formatRarity(loot.chance)}</LootChance>
|
|
196
197
|
</LootItem>
|
|
197
198
|
))}
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
+
import { IFaqItem } from '@rpg-engine/shared';
|
|
1
2
|
import React, { useEffect, useMemo, useState } from 'react';
|
|
2
3
|
import styled from 'styled-components';
|
|
3
4
|
import { Collapsible } from '../../../shared/Collapsible/Collapsible';
|
|
4
5
|
import { PaginatedContent } from '../../../shared/PaginatedContent/PaginatedContent';
|
|
5
6
|
import { SearchHeader } from '../../../shared/SearchHeader/SearchHeader';
|
|
6
|
-
import { IFaqItem } from '../../InformationCenter';
|
|
7
7
|
|
|
8
8
|
interface IFaqSectionProps {
|
|
9
9
|
faqItems: IFaqItem[];
|
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
IInformationCenterItem,
|
|
3
|
+
IInformationCenterNPC,
|
|
4
|
+
isMobileOrTablet,
|
|
5
|
+
} from '@rpg-engine/shared';
|
|
2
6
|
import React from 'react';
|
|
3
7
|
import styled from 'styled-components';
|
|
4
8
|
import { uiColors } from '../../../../constants/uiColors';
|
|
5
9
|
import { Collapsible } from '../../../shared/Collapsible/Collapsible';
|
|
6
|
-
import {
|
|
7
|
-
IInformationCenterItem,
|
|
8
|
-
IInformationCenterNPC,
|
|
9
|
-
} from '../../InformationCenterTypes';
|
|
10
10
|
import { BaseInformationDetails } from '../../shared/BaseInformationDetails';
|
|
11
11
|
|
|
12
12
|
interface IInformationCenterItemDetailsProps {
|
|
@@ -130,7 +130,9 @@ export const InformationCenterItemDetails: React.FC<IInformationCenterItemDetail
|
|
|
130
130
|
<BuffsList>
|
|
131
131
|
{item.equippedBuff.map((buff, index) => (
|
|
132
132
|
<BuffItem key={index}>
|
|
133
|
-
<BuffName>
|
|
133
|
+
<BuffName>
|
|
134
|
+
{buff.trait.replace(/^\w/, c => c.toUpperCase())}
|
|
135
|
+
</BuffName>
|
|
134
136
|
<BuffValue>+{buff.buffPercentage}%</BuffValue>
|
|
135
137
|
</BuffItem>
|
|
136
138
|
))}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { IInformationCenterItem } from '@rpg-engine/shared';
|
|
1
2
|
import React from 'react';
|
|
2
3
|
import styled from 'styled-components';
|
|
3
4
|
import { uiColors } from '../../../../constants/uiColors';
|
|
@@ -8,7 +9,6 @@ import BaseTooltip, {
|
|
|
8
9
|
StatsContainer,
|
|
9
10
|
TooltipTitle,
|
|
10
11
|
} from '../../../shared/BaseTooltip';
|
|
11
|
-
import { IInformationCenterItem } from '../../InformationCenterTypes';
|
|
12
12
|
|
|
13
13
|
interface IItemTooltipProps {
|
|
14
14
|
item: IInformationCenterItem;
|
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
IInformationCenterItem,
|
|
3
|
+
IInformationCenterNPC,
|
|
4
|
+
ItemType,
|
|
5
|
+
} from '@rpg-engine/shared';
|
|
2
6
|
import React, { useState } from 'react';
|
|
3
7
|
import styled from 'styled-components';
|
|
4
8
|
import { IOptionsProps } from '../../../Dropdown';
|
|
5
9
|
import { PaginatedContent } from '../../../shared/PaginatedContent/PaginatedContent';
|
|
6
10
|
import { InformationCenterCell } from '../../InformationCenterCell';
|
|
7
|
-
import {
|
|
8
|
-
IInformationCenterItem,
|
|
9
|
-
IInformationCenterNPC,
|
|
10
|
-
} from '../../InformationCenterTypes';
|
|
11
11
|
import { InformationCenterItemDetails } from './InformationCenterItemDetails';
|
|
12
12
|
import { InformationCenterItemTooltip } from './InformationCenterItemTooltip';
|
|
13
13
|
|
|
14
|
-
const TOOLTIP_OFFSET =
|
|
14
|
+
const TOOLTIP_OFFSET = 200;
|
|
15
15
|
|
|
16
16
|
interface IItemsSectionProps {
|
|
17
17
|
items: IInformationCenterItem[];
|
|
@@ -22,6 +22,23 @@ interface IItemsSectionProps {
|
|
|
22
22
|
tabId: string;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
export const formatItemType = (type: string): string => {
|
|
26
|
+
const words = type.split(/(?=[A-Z])/);
|
|
27
|
+
|
|
28
|
+
if (words.length === 1) {
|
|
29
|
+
return words[0];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
return words
|
|
33
|
+
.map((word, index) => {
|
|
34
|
+
if (index === 0) {
|
|
35
|
+
return word.charAt(0) + '.';
|
|
36
|
+
}
|
|
37
|
+
return word;
|
|
38
|
+
})
|
|
39
|
+
.join(' ');
|
|
40
|
+
};
|
|
41
|
+
|
|
25
42
|
export const InformationCenterItemsSection: React.FC<IItemsSectionProps> = ({
|
|
26
43
|
items,
|
|
27
44
|
bestiaryItems,
|
|
@@ -45,9 +62,11 @@ export const InformationCenterItemsSection: React.FC<IItemsSectionProps> = ({
|
|
|
45
62
|
|
|
46
63
|
const itemCategoryOptions: IOptionsProps[] = [
|
|
47
64
|
{ id: 0, value: 'all', option: 'All Items' },
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
65
|
+
...Object.values(ItemType).map((type, index) => ({
|
|
66
|
+
id: index + 1,
|
|
67
|
+
value: type,
|
|
68
|
+
option: formatItemType(type),
|
|
69
|
+
})),
|
|
51
70
|
];
|
|
52
71
|
|
|
53
72
|
const filteredItems = items.filter(
|
|
@@ -108,6 +127,13 @@ export const InformationCenterItemsSection: React.FC<IItemsSectionProps> = ({
|
|
|
108
127
|
setHoveredItem(null);
|
|
109
128
|
};
|
|
110
129
|
|
|
130
|
+
const handleSearchChange = (newQuery: string) => {
|
|
131
|
+
setSearchQuery(newQuery);
|
|
132
|
+
if (newQuery && selectedItemCategory !== 'all') {
|
|
133
|
+
setSelectedItemCategory('all');
|
|
134
|
+
}
|
|
135
|
+
};
|
|
136
|
+
|
|
111
137
|
const renderItem = (item: IInformationCenterItem) => (
|
|
112
138
|
<InformationCenterCell
|
|
113
139
|
key={item.key}
|
|
@@ -136,7 +162,7 @@ export const InformationCenterItemsSection: React.FC<IItemsSectionProps> = ({
|
|
|
136
162
|
}}
|
|
137
163
|
searchOptions={{
|
|
138
164
|
value: searchQuery,
|
|
139
|
-
onChange:
|
|
165
|
+
onChange: handleSearchChange,
|
|
140
166
|
placeholder: 'Search items...',
|
|
141
167
|
}}
|
|
142
168
|
dependencies={[selectedItemCategory]}
|