@rpg-engine/long-bow 0.8.231 → 0.8.232
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/Quests/QuestInfo/QuestInfo.d.ts +3 -1
- package/dist/components/Quests/QuestInfo/QuestObjectivesSection.d.ts +8 -0
- package/dist/components/Quests/QuestInfo/QuestRequirementsSection.d.ts +8 -0
- package/dist/components/Quests/QuestInfo/QuestRewardsSection.d.ts +8 -0
- package/dist/components/Quests/QuestInfo/QuestSectionTypes.d.ts +8 -0
- package/dist/components/Quests/QuestList.d.ts +4 -5
- package/dist/components/Quests/QuestListRow.d.ts +15 -0
- package/dist/components/Tutorial/TutorialStepper.d.ts +2 -0
- package/dist/index.d.ts +1 -0
- package/dist/long-bow.cjs.development.js +435 -138
- 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 +436 -140
- package/dist/long-bow.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/Quests/QuestInfo/QuestInfo.tsx +57 -68
- package/src/components/Quests/QuestInfo/QuestObjectivesSection.tsx +114 -0
- package/src/components/Quests/QuestInfo/QuestRequirementsSection.tsx +74 -0
- package/src/components/Quests/QuestInfo/QuestRewardsSection.tsx +128 -0
- package/src/components/Quests/QuestInfo/QuestSectionTypes.ts +8 -0
- package/src/components/Quests/QuestList.tsx +12 -87
- package/src/components/Quests/QuestListRow.tsx +178 -0
- package/src/components/Store/CartView.tsx +3 -2
- package/src/components/Store/MetadataCollector.tsx +28 -1
- package/src/components/Store/__test__/MetadataCollector.spec.tsx +12 -3
- package/src/components/Tutorial/TutorialStepper.tsx +2 -0
- package/src/index.tsx +1 -1
package/package.json
CHANGED
|
@@ -10,14 +10,19 @@ import { DraggableContainer } from '../../DraggableContainer';
|
|
|
10
10
|
import { RPGUIContainerTypes } from '../../RPGUI/RPGUIContainer';
|
|
11
11
|
import { Column } from '../../shared/Column';
|
|
12
12
|
import thumbnailDefault from './img/default.png';
|
|
13
|
+
import { QuestObjectivesSection } from './QuestObjectivesSection';
|
|
14
|
+
import { QuestRequirementsSection } from './QuestRequirementsSection';
|
|
15
|
+
import { QuestRewardsSection } from './QuestRewardsSection';
|
|
16
|
+
import { IQuestAtlasProps } from './QuestSectionTypes';
|
|
13
17
|
|
|
14
18
|
export interface IQuestsButtonProps {
|
|
15
19
|
disabled: boolean;
|
|
20
|
+
disabledReason?: string;
|
|
16
21
|
title: string;
|
|
17
22
|
onClick: (questId: string, npcId: string) => void;
|
|
18
23
|
}
|
|
19
24
|
|
|
20
|
-
export interface IQuestInfoProps {
|
|
25
|
+
export interface IQuestInfoProps extends IQuestAtlasProps {
|
|
21
26
|
onClose?: () => void;
|
|
22
27
|
buttons?: IQuestsButtonProps[];
|
|
23
28
|
quests: IQuest[];
|
|
@@ -31,15 +36,22 @@ export const QuestInfo: React.FC<IQuestInfoProps> = ({
|
|
|
31
36
|
buttons,
|
|
32
37
|
onChangeQuest,
|
|
33
38
|
scale,
|
|
39
|
+
itemsAtlasJSON,
|
|
40
|
+
itemsAtlasIMG,
|
|
41
|
+
entitiesAtlasJSON,
|
|
42
|
+
entitiesAtlasIMG,
|
|
43
|
+
iconsAtlasJSON,
|
|
44
|
+
iconsAtlasIMG,
|
|
34
45
|
}) => {
|
|
35
46
|
const [currentIndex, setCurrentIndex] = useState(0);
|
|
36
47
|
const questsLength = quests.length - 1;
|
|
48
|
+
const currentQuest = quests[currentIndex] || quests[0];
|
|
37
49
|
|
|
38
50
|
useEffect(() => {
|
|
39
|
-
if (onChangeQuest) {
|
|
40
|
-
onChangeQuest(currentIndex,
|
|
51
|
+
if (onChangeQuest && currentQuest) {
|
|
52
|
+
onChangeQuest(currentIndex, currentQuest._id);
|
|
41
53
|
}
|
|
42
|
-
}, [currentIndex]);
|
|
54
|
+
}, [currentIndex, currentQuest]);
|
|
43
55
|
|
|
44
56
|
const onLeftClick = () => {
|
|
45
57
|
if (currentIndex === 0) setCurrentIndex(questsLength);
|
|
@@ -60,86 +72,59 @@ export const QuestInfo: React.FC<IQuestInfoProps> = ({
|
|
|
60
72
|
cancelDrag=".equipment-container-body .arrow-selector"
|
|
61
73
|
scale={scale}
|
|
62
74
|
>
|
|
63
|
-
{
|
|
75
|
+
{currentQuest && (
|
|
64
76
|
<QuestsContainer>
|
|
65
|
-
{currentIndex !== 0 && (
|
|
66
|
-
<SelectArrow
|
|
67
|
-
direction="left"
|
|
68
|
-
onPointerDown={onLeftClick}
|
|
69
|
-
></SelectArrow>
|
|
77
|
+
{quests.length >= 2 && currentIndex !== 0 && (
|
|
78
|
+
<SelectArrow direction="left" onPointerDown={onLeftClick} />
|
|
70
79
|
)}
|
|
71
|
-
{currentIndex !== quests.length - 1 && (
|
|
72
|
-
<SelectArrow
|
|
73
|
-
direction="right"
|
|
74
|
-
onPointerDown={onRightClick}
|
|
75
|
-
></SelectArrow>
|
|
80
|
+
{quests.length >= 2 && currentIndex !== quests.length - 1 && (
|
|
81
|
+
<SelectArrow direction="right" onPointerDown={onRightClick} />
|
|
76
82
|
)}
|
|
77
|
-
|
|
78
83
|
<QuestContainer>
|
|
79
84
|
<TitleContainer className="drag-handler">
|
|
80
85
|
<Title>
|
|
81
|
-
<Thumbnail
|
|
82
|
-
|
|
83
|
-
/>
|
|
84
|
-
{quests[currentIndex].title}
|
|
86
|
+
<Thumbnail src={currentQuest.thumbnail || thumbnailDefault} />
|
|
87
|
+
{currentQuest.title}
|
|
85
88
|
</Title>
|
|
86
89
|
<QuestSplitDiv>
|
|
87
90
|
<hr className="golden" />
|
|
88
91
|
</QuestSplitDiv>
|
|
89
92
|
</TitleContainer>
|
|
90
93
|
<Content>
|
|
91
|
-
<p>{
|
|
94
|
+
<p>{currentQuest.description}</p>
|
|
92
95
|
</Content>
|
|
96
|
+
<QuestObjectivesSection
|
|
97
|
+
objectives={currentQuest.objectives as any}
|
|
98
|
+
entitiesAtlasJSON={entitiesAtlasJSON}
|
|
99
|
+
entitiesAtlasIMG={entitiesAtlasIMG}
|
|
100
|
+
/>
|
|
101
|
+
<QuestRequirementsSection
|
|
102
|
+
objectives={currentQuest.objectives as any}
|
|
103
|
+
itemsAtlasJSON={itemsAtlasJSON}
|
|
104
|
+
itemsAtlasIMG={itemsAtlasIMG}
|
|
105
|
+
/>
|
|
106
|
+
<QuestRewardsSection
|
|
107
|
+
rewards={currentQuest.rewards}
|
|
108
|
+
itemsAtlasJSON={itemsAtlasJSON}
|
|
109
|
+
itemsAtlasIMG={itemsAtlasIMG}
|
|
110
|
+
iconsAtlasJSON={iconsAtlasJSON}
|
|
111
|
+
iconsAtlasIMG={iconsAtlasIMG}
|
|
112
|
+
/>
|
|
93
113
|
<QuestColumn className="dark-background" justifyContent="flex-end">
|
|
94
114
|
{buttons &&
|
|
95
115
|
buttons.map((button, index) => (
|
|
96
|
-
<
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
>
|
|
108
|
-
{button.title}
|
|
109
|
-
</Button>
|
|
110
|
-
))}
|
|
111
|
-
</QuestColumn>
|
|
112
|
-
</QuestContainer>
|
|
113
|
-
</QuestsContainer>
|
|
114
|
-
) : (
|
|
115
|
-
<QuestsContainer>
|
|
116
|
-
<QuestContainer>
|
|
117
|
-
<TitleContainer className="drag-handler">
|
|
118
|
-
<Title>
|
|
119
|
-
<Thumbnail src={quests[0].thumbnail || thumbnailDefault} />
|
|
120
|
-
{quests[0].title}
|
|
121
|
-
</Title>
|
|
122
|
-
<QuestSplitDiv>
|
|
123
|
-
<hr className="golden" />
|
|
124
|
-
</QuestSplitDiv>
|
|
125
|
-
</TitleContainer>
|
|
126
|
-
<Content>
|
|
127
|
-
<p>{quests[0].description}</p>
|
|
128
|
-
</Content>
|
|
129
|
-
<QuestColumn className="dark-background" justifyContent="flex-end">
|
|
130
|
-
{buttons &&
|
|
131
|
-
buttons.map((button, index) => (
|
|
132
|
-
<Button
|
|
133
|
-
key={index}
|
|
134
|
-
onPointerDown={() =>
|
|
135
|
-
button.onClick(quests[0]._id, quests[0].npcId)
|
|
136
|
-
}
|
|
137
|
-
disabled={button.disabled}
|
|
138
|
-
buttonType={ButtonTypes.RPGUIButton}
|
|
139
|
-
id={`button-${index}`}
|
|
140
|
-
>
|
|
141
|
-
{button.title}
|
|
142
|
-
</Button>
|
|
116
|
+
<ButtonWrapper key={index} title={button.disabled ? button.disabledReason : undefined}>
|
|
117
|
+
<Button
|
|
118
|
+
onPointerDown={() =>
|
|
119
|
+
button.onClick(currentQuest._id, currentQuest.npcId)
|
|
120
|
+
}
|
|
121
|
+
disabled={button.disabled}
|
|
122
|
+
buttonType={ButtonTypes.RPGUIButton}
|
|
123
|
+
id={`button-${index}`}
|
|
124
|
+
>
|
|
125
|
+
{button.title}
|
|
126
|
+
</Button>
|
|
127
|
+
</ButtonWrapper>
|
|
143
128
|
))}
|
|
144
129
|
</QuestColumn>
|
|
145
130
|
</QuestContainer>
|
|
@@ -207,6 +192,10 @@ const QuestColumn = styled(Column)`
|
|
|
207
192
|
justify-content: space-evenly;
|
|
208
193
|
`;
|
|
209
194
|
|
|
195
|
+
const ButtonWrapper = styled.div`
|
|
196
|
+
display: inline-flex;
|
|
197
|
+
`;
|
|
198
|
+
|
|
210
199
|
const TitleContainer = styled.div`
|
|
211
200
|
width: 100%;
|
|
212
201
|
display: flex;
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import { IQuestObjectiveInteraction, IQuestObjectiveKill, QuestType } from '@rpg-engine/shared';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import { uiColors } from '../../../constants/uiColors';
|
|
5
|
+
import { SpriteFromAtlas } from '../../shared/SpriteFromAtlas';
|
|
6
|
+
import { IQuestAtlasProps } from './QuestSectionTypes';
|
|
7
|
+
|
|
8
|
+
interface IProps extends IQuestAtlasProps {
|
|
9
|
+
objectives?: Array<IQuestObjectiveKill | IQuestObjectiveInteraction>;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export const QuestObjectivesSection: React.FC<IProps> = ({
|
|
13
|
+
objectives = [],
|
|
14
|
+
entitiesAtlasJSON,
|
|
15
|
+
entitiesAtlasIMG,
|
|
16
|
+
}) => {
|
|
17
|
+
if (!objectives.length) {
|
|
18
|
+
return null;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return (
|
|
22
|
+
<Section>
|
|
23
|
+
<SectionTitle>Objectives</SectionTitle>
|
|
24
|
+
{objectives.map((objective: any, index) => {
|
|
25
|
+
if (objective.type === QuestType.Kill) {
|
|
26
|
+
const progress = `${objective.killCount || 0}/${objective.killCountTarget}`;
|
|
27
|
+
const fill = Math.min((objective.killCount || 0) / Math.max(objective.killCountTarget || 1, 1), 1) * 100;
|
|
28
|
+
|
|
29
|
+
return (
|
|
30
|
+
<ObjectiveBlock key={objective.id || index}>
|
|
31
|
+
{(objective.creatures || objective.creatureKeys || []).map((creature: any) => {
|
|
32
|
+
const data = typeof creature === 'string' ? { key: creature, name: creature, texturePath: '' } : creature;
|
|
33
|
+
return (
|
|
34
|
+
<ObjectiveRow key={data.key}>
|
|
35
|
+
{entitiesAtlasJSON && entitiesAtlasIMG && data.texturePath && (
|
|
36
|
+
<SpriteFromAtlas
|
|
37
|
+
atlasJSON={entitiesAtlasJSON}
|
|
38
|
+
atlasIMG={entitiesAtlasIMG}
|
|
39
|
+
spriteKey={data.texturePath}
|
|
40
|
+
width={32}
|
|
41
|
+
height={32}
|
|
42
|
+
imgScale={1}
|
|
43
|
+
centered
|
|
44
|
+
/>
|
|
45
|
+
)}
|
|
46
|
+
<ObjectiveText>{data.name || data.key}</ObjectiveText>
|
|
47
|
+
<ProgressText>{progress}</ProgressText>
|
|
48
|
+
</ObjectiveRow>
|
|
49
|
+
);
|
|
50
|
+
})}
|
|
51
|
+
<ProgressBar>
|
|
52
|
+
<ProgressFill style={{ width: `${fill}%` }} />
|
|
53
|
+
</ProgressBar>
|
|
54
|
+
</ObjectiveBlock>
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
if (objective.type === QuestType.Interaction) {
|
|
59
|
+
return (
|
|
60
|
+
<ObjectiveRow key={objective.id || index}>
|
|
61
|
+
<ObjectiveText>Talk to {objective.targetNPCkey || 'the quest contact'}</ObjectiveText>
|
|
62
|
+
</ObjectiveRow>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return null;
|
|
67
|
+
})}
|
|
68
|
+
</Section>
|
|
69
|
+
);
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const Section = styled.section`
|
|
73
|
+
margin: 10px 18px;
|
|
74
|
+
`;
|
|
75
|
+
|
|
76
|
+
const SectionTitle = styled.h2`
|
|
77
|
+
color: ${uiColors.yellow};
|
|
78
|
+
font-size: 0.9rem;
|
|
79
|
+
margin: 0 0 8px;
|
|
80
|
+
`;
|
|
81
|
+
|
|
82
|
+
const ObjectiveBlock = styled.div`
|
|
83
|
+
margin-bottom: 8px;
|
|
84
|
+
`;
|
|
85
|
+
|
|
86
|
+
const ObjectiveRow = styled.div`
|
|
87
|
+
color: ${uiColors.white};
|
|
88
|
+
display: flex;
|
|
89
|
+
align-items: center;
|
|
90
|
+
gap: 8px;
|
|
91
|
+
min-height: 28px;
|
|
92
|
+
font-size: 0.75rem;
|
|
93
|
+
`;
|
|
94
|
+
|
|
95
|
+
const ObjectiveText = styled.span`
|
|
96
|
+
flex: 1;
|
|
97
|
+
`;
|
|
98
|
+
|
|
99
|
+
const ProgressText = styled.span`
|
|
100
|
+
color: ${uiColors.yellow};
|
|
101
|
+
`;
|
|
102
|
+
|
|
103
|
+
const ProgressBar = styled.div`
|
|
104
|
+
height: 6px;
|
|
105
|
+
background: ${uiColors.gray};
|
|
106
|
+
border-radius: 3px;
|
|
107
|
+
overflow: hidden;
|
|
108
|
+
margin-top: 4px;
|
|
109
|
+
`;
|
|
110
|
+
|
|
111
|
+
const ProgressFill = styled.div`
|
|
112
|
+
height: 100%;
|
|
113
|
+
background: ${uiColors.lightGreen};
|
|
114
|
+
`;
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { IQuestObjectiveInteraction, QuestType } from '@rpg-engine/shared';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import { uiColors } from '../../../constants/uiColors';
|
|
5
|
+
import { ItemInfoWrapper } from '../../Item/Cards/ItemInfoWrapper';
|
|
6
|
+
import { SpriteFromAtlas } from '../../shared/SpriteFromAtlas';
|
|
7
|
+
import { IQuestAtlasProps } from './QuestSectionTypes';
|
|
8
|
+
|
|
9
|
+
interface IProps extends IQuestAtlasProps {
|
|
10
|
+
objectives?: IQuestObjectiveInteraction[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const QuestRequirementsSection: React.FC<IProps> = ({
|
|
14
|
+
objectives = [],
|
|
15
|
+
itemsAtlasJSON,
|
|
16
|
+
itemsAtlasIMG,
|
|
17
|
+
}) => {
|
|
18
|
+
const items = objectives
|
|
19
|
+
.filter((objective: any) => objective.type === QuestType.Interaction)
|
|
20
|
+
.flatMap((objective: any) => objective.items || []);
|
|
21
|
+
|
|
22
|
+
if (!items.length) {
|
|
23
|
+
return null;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
return (
|
|
27
|
+
<Section>
|
|
28
|
+
<SectionTitle>Requirements</SectionTitle>
|
|
29
|
+
{items.map((item: any) => {
|
|
30
|
+
const hasQty = item.playerHasQty || 0;
|
|
31
|
+
const isMet = hasQty >= item.qty;
|
|
32
|
+
return (
|
|
33
|
+
<RequirementRow key={item.itemKey} isMet={isMet}>
|
|
34
|
+
{itemsAtlasJSON && itemsAtlasIMG && item.texturePath && (
|
|
35
|
+
<ItemInfoWrapper item={{ ...item, key: item.itemKey, stackQty: item.qty } as any} atlasJSON={itemsAtlasJSON} atlasIMG={itemsAtlasIMG}>
|
|
36
|
+
<SpriteFromAtlas
|
|
37
|
+
atlasJSON={itemsAtlasJSON}
|
|
38
|
+
atlasIMG={itemsAtlasIMG}
|
|
39
|
+
spriteKey={item.texturePath}
|
|
40
|
+
width={32}
|
|
41
|
+
height={32}
|
|
42
|
+
imgScale={1.5}
|
|
43
|
+
centered
|
|
44
|
+
/>
|
|
45
|
+
</ItemInfoWrapper>
|
|
46
|
+
)}
|
|
47
|
+
<span>
|
|
48
|
+
{hasQty}/{item.qty} {item.name || item.itemKey}
|
|
49
|
+
</span>
|
|
50
|
+
</RequirementRow>
|
|
51
|
+
);
|
|
52
|
+
})}
|
|
53
|
+
</Section>
|
|
54
|
+
);
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
const Section = styled.section`
|
|
58
|
+
margin: 10px 18px;
|
|
59
|
+
`;
|
|
60
|
+
|
|
61
|
+
const SectionTitle = styled.h2`
|
|
62
|
+
color: ${uiColors.yellow};
|
|
63
|
+
font-size: 0.9rem;
|
|
64
|
+
margin: 0 0 8px;
|
|
65
|
+
`;
|
|
66
|
+
|
|
67
|
+
const RequirementRow = styled.div<{ isMet: boolean }>`
|
|
68
|
+
color: ${(props) => (props.isMet ? uiColors.lightGreen : uiColors.red)};
|
|
69
|
+
display: flex;
|
|
70
|
+
align-items: center;
|
|
71
|
+
gap: 8px;
|
|
72
|
+
min-height: 34px;
|
|
73
|
+
font-size: 0.75rem;
|
|
74
|
+
`;
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
import { IQuestReward } from '@rpg-engine/shared';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import styled from 'styled-components';
|
|
4
|
+
import { uiColors } from '../../../constants/uiColors';
|
|
5
|
+
import { ItemInfoWrapper } from '../../Item/Cards/ItemInfoWrapper';
|
|
6
|
+
import { SpriteFromAtlas } from '../../shared/SpriteFromAtlas';
|
|
7
|
+
import { IQuestAtlasProps } from './QuestSectionTypes';
|
|
8
|
+
|
|
9
|
+
interface IProps extends IQuestAtlasProps {
|
|
10
|
+
rewards?: IQuestReward[];
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const QuestRewardsSection: React.FC<IProps> = ({
|
|
14
|
+
rewards = [],
|
|
15
|
+
itemsAtlasJSON,
|
|
16
|
+
itemsAtlasIMG,
|
|
17
|
+
iconsAtlasJSON,
|
|
18
|
+
iconsAtlasIMG,
|
|
19
|
+
}) => {
|
|
20
|
+
const items = rewards.flatMap((reward: any) => reward.items || []);
|
|
21
|
+
const spells = rewards.flatMap((reward: any) => reward.spells || []);
|
|
22
|
+
|
|
23
|
+
if (!items.length && !spells.length) {
|
|
24
|
+
return null;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return (
|
|
28
|
+
<Section>
|
|
29
|
+
<SectionTitle>Rewards</SectionTitle>
|
|
30
|
+
<RewardGrid>
|
|
31
|
+
{items.map((item: any) => (
|
|
32
|
+
<RewardItem key={`${item.key}-${item.qty}`}>
|
|
33
|
+
{itemsAtlasJSON && itemsAtlasIMG && item.texturePath ? (
|
|
34
|
+
<ItemInfoWrapper
|
|
35
|
+
item={{ ...item, key: item.key, stackQty: item.qty } as any}
|
|
36
|
+
atlasJSON={itemsAtlasJSON}
|
|
37
|
+
atlasIMG={itemsAtlasIMG}
|
|
38
|
+
>
|
|
39
|
+
<SpriteFromAtlas
|
|
40
|
+
atlasJSON={itemsAtlasJSON}
|
|
41
|
+
atlasIMG={itemsAtlasIMG}
|
|
42
|
+
spriteKey={item.texturePath}
|
|
43
|
+
width={32}
|
|
44
|
+
height={32}
|
|
45
|
+
imgScale={1.5}
|
|
46
|
+
centered
|
|
47
|
+
/>
|
|
48
|
+
</ItemInfoWrapper>
|
|
49
|
+
) : (
|
|
50
|
+
<FallbackIcon>{item.name?.[0] || '?'}</FallbackIcon>
|
|
51
|
+
)}
|
|
52
|
+
{item.qty > 1 && <QtyBadge>x{item.qty}</QtyBadge>}
|
|
53
|
+
<RewardName>{item.name || item.key}</RewardName>
|
|
54
|
+
</RewardItem>
|
|
55
|
+
))}
|
|
56
|
+
{spells.map((spell: any) => (
|
|
57
|
+
<RewardItem key={spell.key} title={spell.name || spell.key}>
|
|
58
|
+
{iconsAtlasJSON && iconsAtlasIMG && spell.texturePath ? (
|
|
59
|
+
<SpriteFromAtlas
|
|
60
|
+
atlasJSON={iconsAtlasJSON}
|
|
61
|
+
atlasIMG={iconsAtlasIMG}
|
|
62
|
+
spriteKey={spell.texturePath}
|
|
63
|
+
width={32}
|
|
64
|
+
height={32}
|
|
65
|
+
imgScale={1.5}
|
|
66
|
+
centered
|
|
67
|
+
/>
|
|
68
|
+
) : (
|
|
69
|
+
<FallbackIcon>{spell.name?.[0] || '?'}</FallbackIcon>
|
|
70
|
+
)}
|
|
71
|
+
<RewardName>{spell.name || spell.key}</RewardName>
|
|
72
|
+
</RewardItem>
|
|
73
|
+
))}
|
|
74
|
+
</RewardGrid>
|
|
75
|
+
</Section>
|
|
76
|
+
);
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const Section = styled.section`
|
|
80
|
+
margin: 10px 18px;
|
|
81
|
+
`;
|
|
82
|
+
|
|
83
|
+
const SectionTitle = styled.h2`
|
|
84
|
+
color: ${uiColors.yellow};
|
|
85
|
+
font-size: 0.9rem;
|
|
86
|
+
margin: 0 0 8px;
|
|
87
|
+
`;
|
|
88
|
+
|
|
89
|
+
const RewardGrid = styled.div`
|
|
90
|
+
display: grid;
|
|
91
|
+
grid-template-columns: repeat(auto-fill, minmax(92px, 1fr));
|
|
92
|
+
gap: 8px;
|
|
93
|
+
`;
|
|
94
|
+
|
|
95
|
+
const RewardItem = styled.div`
|
|
96
|
+
position: relative;
|
|
97
|
+
min-height: 58px;
|
|
98
|
+
color: ${uiColors.white};
|
|
99
|
+
display: flex;
|
|
100
|
+
flex-direction: column;
|
|
101
|
+
align-items: center;
|
|
102
|
+
gap: 4px;
|
|
103
|
+
font-size: 0.65rem;
|
|
104
|
+
`;
|
|
105
|
+
|
|
106
|
+
const FallbackIcon = styled.div`
|
|
107
|
+
width: 32px;
|
|
108
|
+
height: 32px;
|
|
109
|
+
border: 1px solid ${uiColors.gray};
|
|
110
|
+
display: flex;
|
|
111
|
+
align-items: center;
|
|
112
|
+
justify-content: center;
|
|
113
|
+
color: ${uiColors.yellow};
|
|
114
|
+
`;
|
|
115
|
+
|
|
116
|
+
const QtyBadge = styled.span`
|
|
117
|
+
position: absolute;
|
|
118
|
+
top: 0;
|
|
119
|
+
right: 18px;
|
|
120
|
+
background: ${uiColors.darkGray};
|
|
121
|
+
color: ${uiColors.yellow};
|
|
122
|
+
border: 1px solid ${uiColors.gray};
|
|
123
|
+
padding: 1px 4px;
|
|
124
|
+
`;
|
|
125
|
+
|
|
126
|
+
const RewardName = styled.span`
|
|
127
|
+
text-align: center;
|
|
128
|
+
`;
|
|
@@ -1,10 +1,12 @@
|
|
|
1
|
-
import { IQuest
|
|
1
|
+
import { IQuest } from '@rpg-engine/shared';
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import styled, { CSSProperties } from 'styled-components';
|
|
4
4
|
import { uiColors } from '../../constants/uiColors';
|
|
5
|
+
import { IQuestListAtlasProps, QuestListRow } from './QuestListRow';
|
|
5
6
|
|
|
6
|
-
export interface IQuestListProps {
|
|
7
|
+
export interface IQuestListProps extends IQuestListAtlasProps {
|
|
7
8
|
quests?: IQuest[];
|
|
9
|
+
compact?: boolean;
|
|
8
10
|
styles?: {
|
|
9
11
|
container?: CSSProperties;
|
|
10
12
|
card?: CSSProperties;
|
|
@@ -13,34 +15,18 @@ export interface IQuestListProps {
|
|
|
13
15
|
};
|
|
14
16
|
}
|
|
15
17
|
|
|
16
|
-
export const QuestList: React.FC<IQuestListProps> = ({ quests, styles }) => {
|
|
18
|
+
export const QuestList: React.FC<IQuestListProps> = ({ quests, compact, styles, itemsAtlasJSON, itemsAtlasIMG }) => {
|
|
17
19
|
return (
|
|
18
20
|
<QuestListContainer style={styles?.container}>
|
|
19
21
|
{quests && quests.length > 0 ? (
|
|
20
22
|
quests.map((quest, i) => (
|
|
21
|
-
<
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
<QuestItem>
|
|
29
|
-
<Label style={styles?.label}>Status:</Label>
|
|
30
|
-
<Value
|
|
31
|
-
style={{
|
|
32
|
-
...styles?.value,
|
|
33
|
-
color: getQuestStatusColor(quest.status),
|
|
34
|
-
}}
|
|
35
|
-
>
|
|
36
|
-
{formatQuestStatus(quest.status) ?? 'Unknown'}
|
|
37
|
-
</Value>
|
|
38
|
-
</QuestItem>
|
|
39
|
-
<QuestItem>
|
|
40
|
-
<Label style={styles?.label}>Description:</Label>
|
|
41
|
-
<Value style={styles?.value}>{quest.description}</Value>
|
|
42
|
-
</QuestItem>
|
|
43
|
-
</QuestCard>
|
|
23
|
+
<QuestListRow
|
|
24
|
+
key={quest._id || i}
|
|
25
|
+
quest={quest}
|
|
26
|
+
compact={compact}
|
|
27
|
+
itemsAtlasJSON={itemsAtlasJSON}
|
|
28
|
+
itemsAtlasIMG={itemsAtlasIMG}
|
|
29
|
+
/>
|
|
44
30
|
))
|
|
45
31
|
) : (
|
|
46
32
|
<NoQuestContainer>
|
|
@@ -58,38 +44,6 @@ const QuestListContainer = styled.div`
|
|
|
58
44
|
font-size: 0.7rem;
|
|
59
45
|
`;
|
|
60
46
|
|
|
61
|
-
const QuestCard = styled.div`
|
|
62
|
-
background-color: ${uiColors.darkGray};
|
|
63
|
-
padding: 15px;
|
|
64
|
-
margin-bottom: 10px;
|
|
65
|
-
border-radius: 10px;
|
|
66
|
-
border: 1px solid ${uiColors.gray};
|
|
67
|
-
display: flex;
|
|
68
|
-
flex-direction: column;
|
|
69
|
-
`;
|
|
70
|
-
|
|
71
|
-
const QuestItem = styled.div`
|
|
72
|
-
display: flex;
|
|
73
|
-
margin-bottom: 5px;
|
|
74
|
-
flex-wrap: wrap;
|
|
75
|
-
|
|
76
|
-
&:last-child {
|
|
77
|
-
margin-bottom: 0;
|
|
78
|
-
}
|
|
79
|
-
`;
|
|
80
|
-
|
|
81
|
-
const Label = styled.span`
|
|
82
|
-
font-weight: bold;
|
|
83
|
-
color: ${uiColors.yellow} !important;
|
|
84
|
-
margin-right: 10px;
|
|
85
|
-
`;
|
|
86
|
-
|
|
87
|
-
const Value = styled.span`
|
|
88
|
-
flex-grow: 1;
|
|
89
|
-
color: ${uiColors.white};
|
|
90
|
-
word-wrap: break-word;
|
|
91
|
-
`;
|
|
92
|
-
|
|
93
47
|
const NoQuestContainer = styled.div`
|
|
94
48
|
text-align: center;
|
|
95
49
|
p {
|
|
@@ -97,32 +51,3 @@ const NoQuestContainer = styled.div`
|
|
|
97
51
|
color: ${uiColors.lightGray};
|
|
98
52
|
}
|
|
99
53
|
`;
|
|
100
|
-
|
|
101
|
-
export const formatQuestText = (text: string) => {
|
|
102
|
-
if (!text) return '';
|
|
103
|
-
return text
|
|
104
|
-
.split('-')
|
|
105
|
-
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
|
|
106
|
-
.join(' ');
|
|
107
|
-
};
|
|
108
|
-
|
|
109
|
-
export const getQuestStatusColor = (status?: QuestStatus) => {
|
|
110
|
-
switch (status) {
|
|
111
|
-
case QuestStatus.Pending:
|
|
112
|
-
return uiColors.orange;
|
|
113
|
-
case QuestStatus.InProgress:
|
|
114
|
-
return uiColors.blue;
|
|
115
|
-
case QuestStatus.Completed:
|
|
116
|
-
return uiColors.lightGreen;
|
|
117
|
-
default:
|
|
118
|
-
return uiColors.white;
|
|
119
|
-
}
|
|
120
|
-
};
|
|
121
|
-
|
|
122
|
-
export const formatQuestStatus = (status?: QuestStatus) => {
|
|
123
|
-
if (!status) return '';
|
|
124
|
-
return status
|
|
125
|
-
.split(/(?=[A-Z])/)
|
|
126
|
-
.join(' ')
|
|
127
|
-
.replace(/^\w/, c => c.toUpperCase());
|
|
128
|
-
};
|