@rpg-engine/long-bow 0.2.15 → 0.2.16

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 { IHistoryDialogProps } from '../components/HistoryDialog';
3
+ declare const meta: Meta;
4
+ export default meta;
5
+ export declare const HistoryDialogStore: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react").ReactFramework, IHistoryDialogProps>;
@@ -0,0 +1,5 @@
1
+ import { Meta } from '@storybook/react';
2
+ import { INPCMultiDialogProps } from '../components/NPCDialog/NPCMultiDialog';
3
+ declare const meta: Meta;
4
+ export default meta;
5
+ export declare const NPCMultiDialogStore: import("@storybook/csf").AnnotatedStoryFn<import("@storybook/react").ReactFramework, INPCMultiDialogProps>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpg-engine/long-bow",
3
- "version": "0.2.15",
3
+ "version": "0.2.16",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -83,7 +83,7 @@
83
83
  },
84
84
  "dependencies": {
85
85
  "@rollup/plugin-image": "^2.1.1",
86
- "@rpg-engine/shared": "^0.4.42",
86
+ "@rpg-engine/shared": "^0.4.46",
87
87
  "dayjs": "^1.11.2",
88
88
  "fs-extra": "^10.1.0",
89
89
  "lodash": "^4.17.21",
@@ -0,0 +1,87 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+ import { NPCDialog, NPCDialogType } from './NPCDialog/NPCDialog';
4
+ import { NPCMultiDialog, NPCMultiDialogType } from './NPCDialog/NPCMultiDialog';
5
+ import {
6
+ IQuestionDialog,
7
+ IQuestionDialogAnswer,
8
+ QuestionDialog,
9
+ } from './NPCDialog/QuestionDialog/QuestionDialog';
10
+
11
+ export interface IHistoryDialogProps {
12
+ backgroundImgPath: string;
13
+ fullCoverBackground: boolean;
14
+ questions?: IQuestionDialog[];
15
+ answers?: IQuestionDialogAnswer[];
16
+ text?: string;
17
+ imagePath?: string;
18
+ textAndTypeArray?: NPCMultiDialogType[];
19
+ onClose: () => void;
20
+ }
21
+
22
+ export const HistoryDialog: React.FC<IHistoryDialogProps> = ({
23
+ backgroundImgPath,
24
+ fullCoverBackground,
25
+ questions,
26
+ answers,
27
+ text,
28
+ imagePath,
29
+ textAndTypeArray,
30
+ onClose,
31
+ }) => {
32
+ return (
33
+ <BackgroundContainer
34
+ imgPath={backgroundImgPath}
35
+ fullImg={fullCoverBackground}
36
+ >
37
+ <DialogContainer>
38
+ {textAndTypeArray ? (
39
+ <NPCMultiDialog
40
+ textAndTypeArray={textAndTypeArray}
41
+ onClose={onClose}
42
+ />
43
+ ) : questions && answers ? (
44
+ <QuestionDialog
45
+ questions={questions}
46
+ answers={answers}
47
+ onClose={onClose}
48
+ />
49
+ ) : text && imagePath ? (
50
+ <NPCDialog
51
+ text={text}
52
+ imagePath={imagePath}
53
+ onClose={onClose}
54
+ type={NPCDialogType.TextAndThumbnail}
55
+ />
56
+ ) : (
57
+ <NPCDialog
58
+ text={text}
59
+ onClose={onClose}
60
+ type={NPCDialogType.TextOnly}
61
+ />
62
+ )}
63
+ </DialogContainer>
64
+ </BackgroundContainer>
65
+ );
66
+ };
67
+
68
+ interface IImgContainerProps {
69
+ imgPath: string;
70
+ fullImg: boolean;
71
+ }
72
+
73
+ const BackgroundContainer = styled.div<IImgContainerProps>`
74
+ width: 100%;
75
+ height: 100%;
76
+ background-image: url(${props => props.imgPath});
77
+ background-size: ${props => (props.imgPath ? 'cover' : 'auto')};
78
+ display: flex;
79
+ justify-content: space-evenly;
80
+ align-items: center;
81
+ `;
82
+
83
+ const DialogContainer = styled.div`
84
+ display: flex;
85
+ justify-content: center;
86
+ padding-top: 200px;
87
+ `;
@@ -0,0 +1,159 @@
1
+ import React, { useEffect, useState } from 'react';
2
+ import styled from 'styled-components';
3
+ import { RPGUIContainer, RPGUIContainerTypes } from '../RPGUIContainer';
4
+ import aliceDefaultThumbnail from './img/npcDialog/npcThumbnails/alice.png';
5
+ import pressSpaceGif from './img/space.gif';
6
+ import { NPCDialogText } from './NPCDialogText';
7
+
8
+ export enum ImgSide {
9
+ right = 'right',
10
+ left = 'left',
11
+ }
12
+
13
+ export interface NPCMultiDialogType {
14
+ text: string;
15
+ imagePath?: string;
16
+ imageSide: ImgSide;
17
+ }
18
+
19
+ export interface INPCMultiDialogProps {
20
+ onClose: () => void;
21
+ textAndTypeArray: NPCMultiDialogType[];
22
+ }
23
+
24
+ export const NPCMultiDialog: React.FC<INPCMultiDialogProps> = ({
25
+ onClose,
26
+ textAndTypeArray,
27
+ }) => {
28
+ const [showGoNextIndicator, setShowGoNextIndicator] = useState<boolean>(
29
+ false
30
+ );
31
+ const [slide, setSlide] = useState<number>(0);
32
+
33
+ const onHandleSpacePress = (event: KeyboardEvent) => {
34
+ if (event.code === 'Space') {
35
+ if (slide < textAndTypeArray?.length - 1) {
36
+ setSlide(prev => prev + 1);
37
+ } else {
38
+ // if there's no more text chunks, close the dialog
39
+ onClose();
40
+ }
41
+ }
42
+ };
43
+
44
+ useEffect(() => {
45
+ document.addEventListener('keydown', onHandleSpacePress);
46
+
47
+ return () => document.removeEventListener('keydown', onHandleSpacePress);
48
+ }, [slide]);
49
+
50
+ return (
51
+ <RPGUIContainer
52
+ type={RPGUIContainerTypes.FramedGold}
53
+ width={'50%'}
54
+ height={'180px'}
55
+ >
56
+ <>
57
+ <Container>
58
+ {textAndTypeArray[slide]?.imageSide === 'right' && (
59
+ <>
60
+ <TextContainer flex={'70%'}>
61
+ <NPCDialogText
62
+ onStartStep={() => setShowGoNextIndicator(false)}
63
+ onEndStep={() => setShowGoNextIndicator(true)}
64
+ text={textAndTypeArray[slide].text || 'No text provided.'}
65
+ onClose={() => {
66
+ if (onClose) {
67
+ onClose();
68
+ }
69
+ }}
70
+ />
71
+ </TextContainer>
72
+ <ThumbnailContainer>
73
+ <NPCThumbnail
74
+ src={
75
+ textAndTypeArray[slide].imagePath || aliceDefaultThumbnail
76
+ }
77
+ />
78
+ </ThumbnailContainer>
79
+ {showGoNextIndicator && (
80
+ <PressSpaceIndicator right={'10.5rem'} src={pressSpaceGif} />
81
+ )}
82
+ </>
83
+ )}
84
+ {textAndTypeArray[slide].imageSide === 'left' && (
85
+ <>
86
+ <ThumbnailContainer>
87
+ <NPCThumbnail
88
+ src={
89
+ textAndTypeArray[slide].imagePath || aliceDefaultThumbnail
90
+ }
91
+ />
92
+ </ThumbnailContainer>
93
+ <TextContainer flex={'70%'}>
94
+ <NPCDialogText
95
+ onStartStep={() => setShowGoNextIndicator(false)}
96
+ onEndStep={() => setShowGoNextIndicator(true)}
97
+ text={textAndTypeArray[slide].text || 'No text provided.'}
98
+ onClose={() => {
99
+ if (onClose) {
100
+ onClose();
101
+ }
102
+ }}
103
+ />
104
+ </TextContainer>
105
+ {showGoNextIndicator && (
106
+ <PressSpaceIndicator right={'1rem'} src={pressSpaceGif} />
107
+ )}
108
+ </>
109
+ )}
110
+ </Container>
111
+ )
112
+ </>
113
+ </RPGUIContainer>
114
+ );
115
+ };
116
+
117
+ const Container = styled.div`
118
+ display: flex;
119
+ width: 100%;
120
+ height: 100%;
121
+
122
+ box-sizing: border-box;
123
+ justify-content: center;
124
+ align-items: flex-start;
125
+ position: relative;
126
+ `;
127
+
128
+ interface ITextContainerProps {
129
+ flex: string;
130
+ }
131
+
132
+ const TextContainer = styled.div<ITextContainerProps>`
133
+ flex: ${({ flex }) => flex} 0 0;
134
+ width: 355px;
135
+ `;
136
+
137
+ const ThumbnailContainer = styled.div`
138
+ flex: 30% 0 0;
139
+ display: flex;
140
+ justify-content: flex-end;
141
+ `;
142
+
143
+ const NPCThumbnail = styled.img`
144
+ image-rendering: pixelated;
145
+ height: 128px;
146
+ width: 128px;
147
+ `;
148
+
149
+ interface IPressSpaceIndicatorProps {
150
+ right: string;
151
+ }
152
+
153
+ const PressSpaceIndicator = styled.img<IPressSpaceIndicatorProps>`
154
+ position: absolute;
155
+ right: ${({ right }) => right};
156
+ bottom: 1rem;
157
+ height: 20.7px;
158
+ image-rendering: -webkit-optimize-contrast;
159
+ `;
@@ -1,31 +1,21 @@
1
- import React, { useLayoutEffect, useState } from 'react';
1
+ import { IQuest } from '@rpg-engine/shared';
2
+ import React from 'react';
2
3
  import styled from 'styled-components';
3
4
  import { DraggableContainer } from './DraggableContainer';
4
- import { ProgressBar } from './ProgressBar';
5
5
  import { RPGUIContainerTypes } from './RPGUIContainer';
6
6
 
7
7
  export interface IQuestListProps {
8
- title: string;
9
- description: string;
10
- quests: { title: string; description: string; completed: boolean }[];
8
+ quests: IQuest[];
9
+ onClose: () => void;
11
10
  }
12
11
 
13
- export const QuestList: React.FC<IQuestListProps> = ({ quests }) => {
14
- const [completedQuestsPercentage, setCompletedQuestsPercentage] = useState(0);
15
-
16
- useLayoutEffect(() => {
17
- const totalQuests = quests.length;
18
- const completedQuests = quests.filter(({ completed }) => completed).length;
19
-
20
- const percentage = Math.floor((completedQuests / totalQuests) * 100);
21
-
22
- setCompletedQuestsPercentage(percentage);
23
- }, []);
24
-
12
+ export const QuestList: React.FC<IQuestListProps> = ({ quests, onClose }) => {
25
13
  return (
26
14
  <QuestDraggableContainer
27
15
  type={RPGUIContainerTypes.Framed}
28
- onCloseButton={() => {}}
16
+ onCloseButton={() => {
17
+ if (onClose) onClose();
18
+ }}
29
19
  width="520px"
30
20
  >
31
21
  <div style={{ width: '100%' }}>
@@ -33,20 +23,16 @@ export const QuestList: React.FC<IQuestListProps> = ({ quests }) => {
33
23
  <hr className="golden" />
34
24
 
35
25
  <QuestListContainer>
36
- {quests.map(({ title, description, completed }, i) => (
26
+ {quests.map((quest, i) => (
37
27
  <div className="quest-item" key={i}>
38
- <span className={`quest-number ${completed ? 'completed' : ''}`}>
39
- {i + 1}
40
- </span>
28
+ <span className="quest-number">{i + 1}</span>
41
29
  <div className="quest-detail">
42
- <p className="quest-detail__title">{title}</p>
43
- <p className="quest-detail__description">{description}</p>
30
+ <p className="quest-detail__title">{quest.title}</p>
31
+ <p className="quest-detail__description">{quest.description}</p>
44
32
  </div>
45
33
  </div>
46
34
  ))}
47
35
  </QuestListContainer>
48
-
49
- <ProgressBar max={100} value={completedQuestsPercentage} color="blue" />
50
36
  </div>
51
37
  </QuestDraggableContainer>
52
38
  );
package/src/index.tsx CHANGED
@@ -24,4 +24,6 @@ export * from './components/SkillsContainer';
24
24
  export * from './components/TextArea';
25
25
  export * from './components/Truncate';
26
26
  export * from './components/typography/DynamicText';
27
+ export * from './components/NPCDialog/NPCMultiDialog';
28
+ export * from './components/HistoryDialog';
27
29
  export { useEventListener } from './hooks/useEventListener';
@@ -43,7 +43,7 @@ export const items: IItem[] = [
43
43
  {
44
44
  _id: '629acef1c7c8e8002ff73564',
45
45
  type: ItemType.Weapon,
46
- subType: ItemSubType.Bow,
46
+ subType: ItemSubType.Axe,
47
47
  textureAtlas: 'items',
48
48
  allowedEquipSlotType: [ItemSlotType.LeftHand, ItemSlotType.RightHand],
49
49
  maxStackSize: 1,
@@ -0,0 +1,59 @@
1
+ import { Meta, Story } from '@storybook/react';
2
+ import React from 'react';
3
+ import { RPGUIRoot } from '../components/RPGUIRoot';
4
+ import aliceDefaultThumbnail from '../components/NPCDialog/img/npcDialog/npcThumbnails/alice.png';
5
+ import ImgBackground from '../components/NPCDialog/img/background.png';
6
+ import {
7
+ HistoryDialog,
8
+ IHistoryDialogProps,
9
+ } from '../components/HistoryDialog';
10
+ import {
11
+ ImgSide,
12
+ NPCMultiDialogType,
13
+ } from '../components/NPCDialog/NPCMultiDialog';
14
+
15
+ const meta: Meta = {
16
+ title: 'NPC History Dialog',
17
+ component: HistoryDialog,
18
+ argTypes: {
19
+ text: {
20
+ control: {
21
+ type: 'text',
22
+ },
23
+ },
24
+ },
25
+ };
26
+
27
+ export default meta;
28
+
29
+ const Template: Story<IHistoryDialogProps> = args => (
30
+ <RPGUIRoot>
31
+ <HistoryDialog {...args} />
32
+ </RPGUIRoot>
33
+ );
34
+
35
+ export const HistoryDialogStore = Template.bind({});
36
+
37
+ const textAndTypeArray: NPCMultiDialogType[] = [
38
+ {
39
+ text: 'I have lived here all my life',
40
+ imagePath: aliceDefaultThumbnail,
41
+ imageSide: ImgSide.right,
42
+ },
43
+ {
44
+ text: 'I have lived here all my life',
45
+ imagePath: aliceDefaultThumbnail,
46
+ imageSide: ImgSide.left,
47
+ },
48
+ {
49
+ text: 'I have lived here all my life',
50
+ imagePath: aliceDefaultThumbnail,
51
+ imageSide: ImgSide.right,
52
+ },
53
+ ];
54
+
55
+ HistoryDialogStore.args = {
56
+ textAndTypeArray,
57
+ fullCoverBackground: true,
58
+ backgroundImgPath: ImgBackground,
59
+ };
@@ -0,0 +1,71 @@
1
+ import { Meta, Story } from '@storybook/react';
2
+ import React from 'react';
3
+ import { RPGUIRoot } from '../../src/components/RPGUIRoot';
4
+ import aliceDefaultThumbnail from '../components/NPCDialog/img/npcDialog/npcThumbnails/alice.png';
5
+ import {
6
+ ImgSide,
7
+ INPCMultiDialogProps,
8
+ NPCMultiDialog,
9
+ NPCMultiDialogType,
10
+ } from '../components/NPCDialog/NPCMultiDialog';
11
+
12
+ const meta: Meta = {
13
+ title: 'NPC MultiDialog',
14
+ component: NPCMultiDialog,
15
+ argTypes: {
16
+ text: {
17
+ control: {
18
+ type: 'text',
19
+ },
20
+ },
21
+ // ImgSide: {
22
+ // control: {
23
+ // type: 'select',
24
+ // label: {
25
+ // [ImgSide.right]: 'right',
26
+ // [ImgSide.left]: 'left',
27
+ // },
28
+ // },
29
+ // },
30
+ imagePath: {
31
+ control: {
32
+ type: 'text',
33
+ },
34
+ },
35
+ },
36
+ };
37
+
38
+ export default meta;
39
+
40
+ const Template: Story<INPCMultiDialogProps> = args => (
41
+ <RPGUIRoot>
42
+ <NPCMultiDialog {...args} />
43
+ </RPGUIRoot>
44
+ );
45
+
46
+ export const NPCMultiDialogStore = Template.bind({});
47
+
48
+ // By passing using the Args format for exported stories, you can control the props for a component for reuse in a test
49
+ // https://storybook.js.org/docs/react/workflows/unit-testing
50
+
51
+ const textAndTypeArray: NPCMultiDialogType[] = [
52
+ {
53
+ text: 'I have lived here all my life',
54
+ imagePath: aliceDefaultThumbnail,
55
+ imageSide: ImgSide.right,
56
+ },
57
+ {
58
+ text: 'I have lived here all my life',
59
+ imagePath: aliceDefaultThumbnail,
60
+ imageSide: ImgSide.left,
61
+ },
62
+ {
63
+ text: 'I have lived here all my life',
64
+ imagePath: aliceDefaultThumbnail,
65
+ imageSide: ImgSide.right,
66
+ },
67
+ ];
68
+
69
+ NPCMultiDialogStore.args = {
70
+ textAndTypeArray,
71
+ };
@@ -1,3 +1,4 @@
1
+ import { QuestStatus, QuestType } from '@rpg-engine/shared';
1
2
  import { Story } from '@storybook/react';
2
3
  import React from 'react';
3
4
  import { IQuestListProps, QuestList } from '../components/QuestList';
@@ -16,39 +17,66 @@ const Template: Story<IQuestListProps> = args => (
16
17
 
17
18
  export const Default = Template.bind({});
18
19
 
20
+ const IQuestMock = [
21
+ {
22
+ _id: '6317996c77f2420047a20b55',
23
+ rewards: [
24
+ {
25
+ id: '6317996c77f2420047a20b53',
26
+ itemKeys: ['6317996c77f2420047a20b53'],
27
+ spellKeys: ['6317996c77f2420047a20b53'],
28
+ qty: 1,
29
+ },
30
+ ],
31
+ objectives: [
32
+ {
33
+ id: '6317996c77f2420047a20b53',
34
+ type: QuestType.Kill,
35
+ status: QuestStatus.Pending,
36
+ killCount: 10,
37
+ killCountTarget: 30,
38
+ creatureKeys: ['6317996c77f2420047a20b53'],
39
+ },
40
+ ],
41
+ npcId: '6317996b77f2420047a20825',
42
+ title: 'Deliver message to the trader1',
43
+ key: 'interaction-trader',
44
+ description:
45
+ "Need to send a message to my brother, the trader, about my father's health. I cannot do it because it is too dangerous out there. Please, help me by delivering this message to him.",
46
+ createdAt: '2022-09-06T19:03:08.285+0000',
47
+ updatedAt: '2022-09-06T19:03:08.304+0000',
48
+ },
49
+ {
50
+ _id: '6317996c77f2420047a20b55',
51
+ rewards: [
52
+ {
53
+ id: '6317996c77f2420047a20b53',
54
+ itemKeys: ['6317996c77f2420047a20b53'],
55
+ spellKeys: ['6317996c77f2420047a20b53'],
56
+ qty: 1,
57
+ },
58
+ ],
59
+ objectives: [
60
+ {
61
+ id: '6317996c77f2420047a20b53',
62
+ type: QuestType.Kill,
63
+ status: QuestStatus.Pending,
64
+ killCount: 10,
65
+ killCountTarget: 30,
66
+ creatureKeys: ['6317996c77f2420047a20b53'],
67
+ },
68
+ ],
69
+ npcId: '6317996b77f2420047a20825',
70
+ title: 'Deliver message to the trader2',
71
+ key: 'interaction-trader',
72
+ description:
73
+ "Need to send a message to my brother, the trader, about my father's health. I cannot do it because it is too dangerous out there. Please, help me by delivering this message to him.",
74
+ createdAt: '2022-09-06T19:03:08.285+0000',
75
+ updatedAt: '2022-09-06T19:03:08.304+0000',
76
+ },
77
+ ];
78
+
19
79
  Default.args = {
20
- title: 'War of shadows',
21
- description: 'The shadow of war missin completed',
22
- quests: [
23
- {
24
- title: 'Shrine of the storm: Whispers below',
25
- description: 'Defeat Lord Stormsong',
26
- completed: true,
27
- },
28
- {
29
- title: 'Shrine of the storm: Whispers below',
30
- description: 'Defeat Lord Stormsong',
31
- completed: false,
32
- },
33
- {
34
- title: 'Shrine of the storm: Whispers below',
35
- description: 'Defeat Lord Stormsong',
36
- completed: false,
37
- },
38
- {
39
- title: 'Shrine of the storm: Whispers below',
40
- description: 'Defeat Lord Stormsong',
41
- completed: false,
42
- },
43
- {
44
- title: 'Shrine of the storm: Whispers below',
45
- description: 'Defeat Lord Stormsong',
46
- completed: false,
47
- },
48
- {
49
- title: 'Shrine of the storm: Whispers below',
50
- description: 'Defeat Lord Stormsong',
51
- completed: false,
52
- },
53
- ],
80
+ quests: IQuestMock,
81
+ onClose: () => console.log('closing'),
54
82
  };