@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.
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpg-engine/long-bow",
3
- "version": "0.8.49",
3
+ "version": "0.8.51",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -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, setLocalTasks] = React.useState(tasks);
40
+ const [localTasks] = React.useState(tasks);
42
41
  const size = useResponsiveSize(scale);
43
42
 
44
43
  const handleClaimReward = (taskKey: string, type: TaskType) => {
45
- setLocalTasks(prevTasks =>
46
- prevTasks.map(task =>
47
- task.key === taskKey ? { ...task, claimed: true } : task
48
- )
49
- );
50
- onClaimReward({ type, taskKey });
44
+ onClaimReward({
45
+ type,
46
+ taskKey,
47
+ });
51
48
  };
52
49
 
53
- const handleClaimAllRewards = () => {
54
- const tasksToReward = localTasks
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={handleClaimAllRewards}
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 && allClaimed && (
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
- key: 'exp',
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: 'Wood',
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
- key: 'exp',
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
- key: 'exp',
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': 2 },
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
- key: 'exp',
208
+ texturePath: 'others/royal-chalice.png',
206
209
  quantity: 200
207
210
  }
208
211
  ],
209
- status: TaskStatus.InProgress,
210
- claimed: false
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
+ };