@rpg-engine/long-bow 0.8.49 → 0.8.51
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/Character/CharacterSkinSelectionModal.d.ts +12 -0
- package/dist/index.d.ts +1 -0
- package/dist/long-bow.cjs.development.js +198 -118
- 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 +198 -119
- package/dist/long-bow.esm.js.map +1 -1
- package/dist/stories/Character/character/CharacterSkinSelectionModal.stories.d.ts +5 -0
- package/package.json +1 -1
- package/src/components/Character/CharacterSkinSelectionModal.tsx +151 -0
- package/src/components/DailyTasks/DailyRewardsTooltip.tsx +3 -8
- package/src/components/DailyTasks/DailyTasks.tsx +8 -18
- package/src/components/DailyTasks/GlobalDailyProgress.tsx +2 -2
- package/src/components/DailyTasks/TaskProgressDetails.tsx +1 -1
- package/src/index.tsx +1 -0
- package/src/mocks/dailyTasks.mocks.ts +12 -9
- package/src/stories/Character/character/CharacterSkinSelectionModal.stories.tsx +52 -0
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
import { Meta } from '@storybook/react';
|
|
2
|
+
import { ICharacterSkinSelectionModalProps } from '../../../components/Character/CharacterSkinSelectionModal';
|
|
3
|
+
declare const meta: Meta;
|
|
4
|
+
export default meta;
|
|
5
|
+
export declare const KnightSkins: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react").ReactFramework, ICharacterSkinSelectionModalProps>;
|
package/package.json
CHANGED
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
import React, { useEffect, useState } from 'react';
|
|
2
|
+
import styled from 'styled-components';
|
|
3
|
+
import { Button, ButtonTypes } from '../Button';
|
|
4
|
+
import { ErrorBoundary } from '../Item/Inventory/ErrorBoundary';
|
|
5
|
+
import PropertySelect, {
|
|
6
|
+
IPropertiesProps,
|
|
7
|
+
} from '../PropertySelect/PropertySelect';
|
|
8
|
+
import { SpriteFromAtlas } from '../shared/SpriteFromAtlas';
|
|
9
|
+
import { ICharacterProps } from './CharacterSelection';
|
|
10
|
+
|
|
11
|
+
export interface ICharacterSkinSelectionModalProps {
|
|
12
|
+
isOpen: boolean;
|
|
13
|
+
onClose: () => void;
|
|
14
|
+
onConfirm: (textureKey: string) => void;
|
|
15
|
+
availableCharacters: ICharacterProps[];
|
|
16
|
+
atlasJSON: any;
|
|
17
|
+
atlasIMG: any;
|
|
18
|
+
initialSelectedSkin?: string;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export const CharacterSkinSelectionModal: React.FC<ICharacterSkinSelectionModalProps> = ({
|
|
22
|
+
isOpen,
|
|
23
|
+
onClose,
|
|
24
|
+
onConfirm,
|
|
25
|
+
availableCharacters,
|
|
26
|
+
atlasJSON,
|
|
27
|
+
atlasIMG,
|
|
28
|
+
initialSelectedSkin = '',
|
|
29
|
+
}) => {
|
|
30
|
+
// Convert availableCharacters to the format used by PropertySelect
|
|
31
|
+
const propertySelectValues = availableCharacters.map(item => ({
|
|
32
|
+
id: item.textureKey,
|
|
33
|
+
name: item.name,
|
|
34
|
+
}));
|
|
35
|
+
|
|
36
|
+
// State to store the selected skin and the sprite key
|
|
37
|
+
const [selectedValue, setSelectedValue] = useState<
|
|
38
|
+
IPropertiesProps | undefined
|
|
39
|
+
>();
|
|
40
|
+
const [selectedSpriteKey, setSelectedSpriteKey] = useState('');
|
|
41
|
+
|
|
42
|
+
// Update sprite when the selected value changes
|
|
43
|
+
const updateSelectedSpriteKey = () => {
|
|
44
|
+
const textureKey = selectedValue ? selectedValue.id : '';
|
|
45
|
+
const spriteKey = textureKey ? textureKey + '/down/standing/0.png' : '';
|
|
46
|
+
|
|
47
|
+
if (spriteKey === selectedSpriteKey) {
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
setSelectedSpriteKey(spriteKey);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
// Update sprite when selectedValue changes
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
updateSelectedSpriteKey();
|
|
57
|
+
}, [selectedValue]);
|
|
58
|
+
|
|
59
|
+
// Initialize selectedValue
|
|
60
|
+
useEffect(() => {
|
|
61
|
+
if (initialSelectedSkin) {
|
|
62
|
+
const initialProperty = propertySelectValues.find(
|
|
63
|
+
prop => prop.id === initialSelectedSkin
|
|
64
|
+
);
|
|
65
|
+
setSelectedValue(initialProperty || propertySelectValues[0]);
|
|
66
|
+
} else if (propertySelectValues.length > 0) {
|
|
67
|
+
setSelectedValue(propertySelectValues[0]);
|
|
68
|
+
}
|
|
69
|
+
}, [initialSelectedSkin, availableCharacters]);
|
|
70
|
+
|
|
71
|
+
// Functions to handle confirmation and cancellation
|
|
72
|
+
const handleConfirm = () => {
|
|
73
|
+
if (selectedValue) {
|
|
74
|
+
onConfirm(selectedValue.id);
|
|
75
|
+
onClose();
|
|
76
|
+
}
|
|
77
|
+
};
|
|
78
|
+
|
|
79
|
+
const handleCancel = () => {
|
|
80
|
+
onClose();
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
if (!isOpen) return null;
|
|
84
|
+
|
|
85
|
+
return (
|
|
86
|
+
<Container>
|
|
87
|
+
{selectedSpriteKey && atlasIMG && atlasJSON && (
|
|
88
|
+
<ErrorBoundary>
|
|
89
|
+
<SpriteFromAtlas
|
|
90
|
+
spriteKey={selectedSpriteKey}
|
|
91
|
+
atlasIMG={atlasIMG}
|
|
92
|
+
atlasJSON={atlasJSON}
|
|
93
|
+
imgScale={4}
|
|
94
|
+
height={80}
|
|
95
|
+
width={64}
|
|
96
|
+
containerStyle={{
|
|
97
|
+
display: 'flex',
|
|
98
|
+
alignItems: 'center',
|
|
99
|
+
paddingBottom: '15px',
|
|
100
|
+
}}
|
|
101
|
+
imgStyle={{
|
|
102
|
+
left: '22px',
|
|
103
|
+
}}
|
|
104
|
+
/>
|
|
105
|
+
</ErrorBoundary>
|
|
106
|
+
)}
|
|
107
|
+
|
|
108
|
+
<PropertySelect
|
|
109
|
+
availableProperties={propertySelectValues}
|
|
110
|
+
onChange={value => {
|
|
111
|
+
setSelectedValue(value);
|
|
112
|
+
}}
|
|
113
|
+
/>
|
|
114
|
+
|
|
115
|
+
<ButtonsContainer>
|
|
116
|
+
<Button buttonType={ButtonTypes.RPGUIButton} onClick={handleCancel}>
|
|
117
|
+
Cancel
|
|
118
|
+
</Button>
|
|
119
|
+
<Button
|
|
120
|
+
buttonType={ButtonTypes.RPGUIButton}
|
|
121
|
+
onClick={handleConfirm}
|
|
122
|
+
disabled={!selectedValue}
|
|
123
|
+
>
|
|
124
|
+
Confirm
|
|
125
|
+
</Button>
|
|
126
|
+
</ButtonsContainer>
|
|
127
|
+
</Container>
|
|
128
|
+
);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
// Styled components
|
|
132
|
+
|
|
133
|
+
const Container = styled.div`
|
|
134
|
+
display: flex;
|
|
135
|
+
flex-direction: column;
|
|
136
|
+
align-items: center;
|
|
137
|
+
image-rendering: pixelated;
|
|
138
|
+
`;
|
|
139
|
+
|
|
140
|
+
const ButtonsContainer = styled.div`
|
|
141
|
+
display: flex;
|
|
142
|
+
justify-content: center;
|
|
143
|
+
gap: 0.8rem;
|
|
144
|
+
width: 100%;
|
|
145
|
+
margin: 3rem 0 0.75rem;
|
|
146
|
+
|
|
147
|
+
button {
|
|
148
|
+
min-width: 95px;
|
|
149
|
+
font-size: 0.9rem;
|
|
150
|
+
}
|
|
151
|
+
`;
|
|
@@ -57,13 +57,7 @@ export const DailyRewardsTooltip: React.FC<IDailyRewardsTooltipProps> = ({
|
|
|
57
57
|
<SpriteFromAtlas
|
|
58
58
|
atlasJSON={itemsAtlasJSON}
|
|
59
59
|
atlasIMG={itemsAtlasIMG}
|
|
60
|
-
spriteKey={
|
|
61
|
-
reward.type === RewardType.Gold
|
|
62
|
-
? 'others/gold-coin-qty-6.png'
|
|
63
|
-
: reward.type === RewardType.Experience
|
|
64
|
-
? 'others/royal-chalice.png'
|
|
65
|
-
: reward.texturePath || 'others/no-image.png'
|
|
66
|
-
}
|
|
60
|
+
spriteKey={reward.texturePath ?? 'check.png'}
|
|
67
61
|
width={24}
|
|
68
62
|
height={24}
|
|
69
63
|
imgScale={1.75}
|
|
@@ -71,7 +65,7 @@ export const DailyRewardsTooltip: React.FC<IDailyRewardsTooltipProps> = ({
|
|
|
71
65
|
<ItemContent>
|
|
72
66
|
<RewardLabel>
|
|
73
67
|
<Ellipsis maxWidth="100%" maxLines={2}>
|
|
74
|
-
{formatTaskKey(reward.key ??
|
|
68
|
+
{formatTaskKey(reward.key ?? reward.type)}:
|
|
75
69
|
</Ellipsis>
|
|
76
70
|
</RewardLabel>
|
|
77
71
|
</ItemContent>
|
|
@@ -142,6 +136,7 @@ const RewardLabel = styled.span`
|
|
|
142
136
|
color: ${uiColors.yellow};
|
|
143
137
|
font-size: 0.9rem;
|
|
144
138
|
line-height: 1.2;
|
|
139
|
+
padding-top: 15px;
|
|
145
140
|
display: inline-flex;
|
|
146
141
|
align-items: center;
|
|
147
142
|
`;
|
|
@@ -2,7 +2,6 @@ import {
|
|
|
2
2
|
ICharacterDailyTask,
|
|
3
3
|
ICollectGlobalRewardRequest,
|
|
4
4
|
ITaskIdentifier,
|
|
5
|
-
TaskStatus,
|
|
6
5
|
TaskType,
|
|
7
6
|
} from '@rpg-engine/shared';
|
|
8
7
|
import React from 'react';
|
|
@@ -38,27 +37,18 @@ export const DailyTasks: React.FC<IDailyTasksProps> = ({
|
|
|
38
37
|
iconAtlasJSON,
|
|
39
38
|
iconAtlasIMG,
|
|
40
39
|
}): JSX.Element | null => {
|
|
41
|
-
const [localTasks
|
|
40
|
+
const [localTasks] = React.useState(tasks);
|
|
42
41
|
const size = useResponsiveSize(scale);
|
|
43
42
|
|
|
44
43
|
const handleClaimReward = (taskKey: string, type: TaskType) => {
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
);
|
|
50
|
-
onClaimReward({ type, taskKey });
|
|
44
|
+
onClaimReward({
|
|
45
|
+
type,
|
|
46
|
+
taskKey,
|
|
47
|
+
});
|
|
51
48
|
};
|
|
52
49
|
|
|
53
|
-
const
|
|
54
|
-
|
|
55
|
-
.filter(task => task.status === TaskStatus.Completed && !task.claimed)
|
|
56
|
-
.map(task => ({
|
|
57
|
-
type: task.type,
|
|
58
|
-
taskKey: task.key,
|
|
59
|
-
}));
|
|
60
|
-
|
|
61
|
-
onClaimGlobalReward({ tasks: tasksToReward });
|
|
50
|
+
const handleClaimGlobalRewards = (tasks: ICollectGlobalRewardRequest) => {
|
|
51
|
+
onClaimGlobalReward(tasks);
|
|
62
52
|
};
|
|
63
53
|
|
|
64
54
|
if (!size) return null;
|
|
@@ -77,7 +67,7 @@ export const DailyTasks: React.FC<IDailyTasksProps> = ({
|
|
|
77
67
|
<TasksList className="tasks-container">
|
|
78
68
|
<GlobalDailyProgress
|
|
79
69
|
tasks={localTasks}
|
|
80
|
-
onClaimAllRewards={
|
|
70
|
+
onClaimAllRewards={handleClaimGlobalRewards}
|
|
81
71
|
/>
|
|
82
72
|
{localTasks.map(task => (
|
|
83
73
|
<DailyTaskItem
|
|
@@ -44,9 +44,9 @@ export const GlobalDailyProgress: React.FC<IGlobalTaskProgressProps> = ({
|
|
|
44
44
|
<ProgressBar>
|
|
45
45
|
<ProgressFill percentage={(completedTasks / totalTasks) * 100} />
|
|
46
46
|
</ProgressBar>
|
|
47
|
-
{totalTasks > 0 && allCompleted &&
|
|
47
|
+
{totalTasks > 0 && allCompleted && (
|
|
48
48
|
<>
|
|
49
|
-
{isClaimed ? (
|
|
49
|
+
{allClaimed || isClaimed ? (
|
|
50
50
|
<ClaimedText>Global Rewards Claimed</ClaimedText>
|
|
51
51
|
) : (
|
|
52
52
|
<CollectWrapper>
|
|
@@ -48,7 +48,7 @@ export const TaskProgressDetails: React.FC<ITaskProgressDetailsProps> = ({
|
|
|
48
48
|
return Object.entries((progress as ICollectProgress).collected || {}).map(
|
|
49
49
|
([key, value], index) => (
|
|
50
50
|
<ProgressItem key={index}>
|
|
51
|
-
<span>{key}:</span>
|
|
51
|
+
<span>{formatTaskKey(key)}:</span>
|
|
52
52
|
<ProgressCount>
|
|
53
53
|
{value}/
|
|
54
54
|
{(task.requirements as ICollectItemsRequirement).targets.find(
|
package/src/index.tsx
CHANGED
|
@@ -2,6 +2,7 @@ export * from './components/Arrow/SelectArrow';
|
|
|
2
2
|
export * from './components/AsyncDropdown';
|
|
3
3
|
export * from './components/Button';
|
|
4
4
|
export * from './components/Character/CharacterSelection';
|
|
5
|
+
export * from './components/Character/CharacterSkinSelectionModal';
|
|
5
6
|
export * from './components/Chat/Chat';
|
|
6
7
|
export * from './components/Chatdeprecated/ChatDeprecated';
|
|
7
8
|
export * from './components/ChatRevamp/ChatRevamp';
|
|
@@ -36,7 +36,7 @@ export const mockTasks: ICharacterDailyTask[] = [
|
|
|
36
36
|
},
|
|
37
37
|
{
|
|
38
38
|
type: RewardType.Experience,
|
|
39
|
-
|
|
39
|
+
texturePath: 'others/royal-chalice.png',
|
|
40
40
|
quantity: 100
|
|
41
41
|
}
|
|
42
42
|
]
|
|
@@ -51,7 +51,7 @@ export const mockTasks: ICharacterDailyTask[] = [
|
|
|
51
51
|
type: TaskType.CollectItems,
|
|
52
52
|
targets: [
|
|
53
53
|
{
|
|
54
|
-
key: '
|
|
54
|
+
key: 'wooden-log',
|
|
55
55
|
quantity: 10
|
|
56
56
|
}
|
|
57
57
|
]
|
|
@@ -63,6 +63,7 @@ export const mockTasks: ICharacterDailyTask[] = [
|
|
|
63
63
|
{
|
|
64
64
|
type: RewardType.Gold,
|
|
65
65
|
key: 'gold-coin',
|
|
66
|
+
texturePath: 'others/gold-coin-qty-6.png',
|
|
66
67
|
quantity: 100
|
|
67
68
|
},
|
|
68
69
|
{
|
|
@@ -73,7 +74,7 @@ export const mockTasks: ICharacterDailyTask[] = [
|
|
|
73
74
|
},
|
|
74
75
|
{
|
|
75
76
|
type: RewardType.Experience,
|
|
76
|
-
|
|
77
|
+
texturePath: 'others/royal-chalice.png',
|
|
77
78
|
quantity: 250
|
|
78
79
|
}
|
|
79
80
|
],
|
|
@@ -102,6 +103,7 @@ export const mockTasks: ICharacterDailyTask[] = [
|
|
|
102
103
|
{
|
|
103
104
|
type: RewardType.Gold,
|
|
104
105
|
key: 'gold-coin',
|
|
106
|
+
texturePath: 'others/gold-coin-qty-6.png',
|
|
105
107
|
quantity: 1000
|
|
106
108
|
},
|
|
107
109
|
{
|
|
@@ -112,7 +114,7 @@ export const mockTasks: ICharacterDailyTask[] = [
|
|
|
112
114
|
},
|
|
113
115
|
{
|
|
114
116
|
type: RewardType.Experience,
|
|
115
|
-
|
|
117
|
+
texturePath: 'others/royal-chalice.png',
|
|
116
118
|
quantity: 1000
|
|
117
119
|
}
|
|
118
120
|
],
|
|
@@ -132,13 +134,13 @@ export const mockTasks: ICharacterDailyTask[] = [
|
|
|
132
134
|
rewards: [
|
|
133
135
|
{
|
|
134
136
|
type: RewardType.Experience,
|
|
135
|
-
key: 'exp',
|
|
136
137
|
texturePath: 'others/royal-chalice.png',
|
|
137
138
|
quantity: 10000
|
|
138
139
|
},
|
|
139
140
|
{
|
|
140
141
|
type: RewardType.Gold,
|
|
141
142
|
key: 'gold-coin',
|
|
143
|
+
texturePath: 'others/gold-coin-qty-6.png',
|
|
142
144
|
quantity: 1000
|
|
143
145
|
},
|
|
144
146
|
{
|
|
@@ -186,7 +188,7 @@ export const mockTasks: ICharacterDailyTask[] = [
|
|
|
186
188
|
]
|
|
187
189
|
},
|
|
188
190
|
progress: {
|
|
189
|
-
crafted: { 'life-potion':
|
|
191
|
+
crafted: { 'life-potion': 5 },
|
|
190
192
|
},
|
|
191
193
|
rewards: [
|
|
192
194
|
{
|
|
@@ -198,15 +200,16 @@ export const mockTasks: ICharacterDailyTask[] = [
|
|
|
198
200
|
{
|
|
199
201
|
type: RewardType.Gold,
|
|
200
202
|
key: 'gold-coin',
|
|
203
|
+
texturePath: 'others/gold-coin-qty-6.png',
|
|
201
204
|
quantity: 150
|
|
202
205
|
},
|
|
203
206
|
{
|
|
204
207
|
type: RewardType.Experience,
|
|
205
|
-
|
|
208
|
+
texturePath: 'others/royal-chalice.png',
|
|
206
209
|
quantity: 200
|
|
207
210
|
}
|
|
208
211
|
],
|
|
209
|
-
status: TaskStatus.
|
|
210
|
-
claimed:
|
|
212
|
+
status: TaskStatus.Completed,
|
|
213
|
+
claimed: true
|
|
211
214
|
},
|
|
212
215
|
];
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { Meta, Story } from '@storybook/react';
|
|
2
|
+
import React from 'react';
|
|
3
|
+
import { RPGUIRoot } from '../../..';
|
|
4
|
+
import {
|
|
5
|
+
CharacterSkinSelectionModal,
|
|
6
|
+
ICharacterSkinSelectionModalProps,
|
|
7
|
+
} from '../../../components/Character/CharacterSkinSelectionModal';
|
|
8
|
+
import atlasJSON from '../../../mocks/atlas/entities/entities.json';
|
|
9
|
+
import atlasIMG from '../../../mocks/atlas/entities/entities.png';
|
|
10
|
+
|
|
11
|
+
const meta: Meta = {
|
|
12
|
+
title: 'Character/Character/Skin Selection',
|
|
13
|
+
component: CharacterSkinSelectionModal,
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export default meta;
|
|
17
|
+
|
|
18
|
+
const Template: Story<ICharacterSkinSelectionModalProps> = (
|
|
19
|
+
args: ICharacterSkinSelectionModalProps
|
|
20
|
+
) => (
|
|
21
|
+
<RPGUIRoot>
|
|
22
|
+
<CharacterSkinSelectionModal {...args} />
|
|
23
|
+
</RPGUIRoot>
|
|
24
|
+
);
|
|
25
|
+
|
|
26
|
+
export const KnightSkins = Template.bind({});
|
|
27
|
+
|
|
28
|
+
// Example of different knight skins
|
|
29
|
+
const knightCharacters = [
|
|
30
|
+
{
|
|
31
|
+
name: 'Black Knight',
|
|
32
|
+
textureKey: 'black-knight',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
name: 'Dragon Knight',
|
|
36
|
+
textureKey: 'dragon-knight',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
name: 'Senior Knight',
|
|
40
|
+
textureKey: 'senior-knight-1',
|
|
41
|
+
},
|
|
42
|
+
];
|
|
43
|
+
|
|
44
|
+
KnightSkins.args = {
|
|
45
|
+
isOpen: true,
|
|
46
|
+
onClose: () => console.log('Modal closed'),
|
|
47
|
+
onConfirm: (textureKey: string) => console.log('Selected skin:', textureKey),
|
|
48
|
+
availableCharacters: knightCharacters,
|
|
49
|
+
atlasJSON: atlasJSON,
|
|
50
|
+
atlasIMG: atlasIMG,
|
|
51
|
+
initialSelectedSkin: 'black-knight',
|
|
52
|
+
};
|