@rpg-engine/long-bow 0.8.230 → 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.
Files changed (30) hide show
  1. package/dist/components/Quests/QuestInfo/QuestInfo.d.ts +3 -1
  2. package/dist/components/Quests/QuestInfo/QuestObjectivesSection.d.ts +8 -0
  3. package/dist/components/Quests/QuestInfo/QuestRequirementsSection.d.ts +8 -0
  4. package/dist/components/Quests/QuestInfo/QuestRewardsSection.d.ts +8 -0
  5. package/dist/components/Quests/QuestInfo/QuestSectionTypes.d.ts +8 -0
  6. package/dist/components/Quests/QuestList.d.ts +4 -5
  7. package/dist/components/Quests/QuestListRow.d.ts +15 -0
  8. package/dist/components/Tutorial/TutorialStepper.d.ts +2 -0
  9. package/dist/index.d.ts +1 -0
  10. package/dist/long-bow.cjs.development.js +473 -156
  11. package/dist/long-bow.cjs.development.js.map +1 -1
  12. package/dist/long-bow.cjs.production.min.js +1 -1
  13. package/dist/long-bow.cjs.production.min.js.map +1 -1
  14. package/dist/long-bow.esm.js +474 -158
  15. package/dist/long-bow.esm.js.map +1 -1
  16. package/package.json +1 -1
  17. package/src/components/Item/Cards/ItemTooltip.tsx +27 -16
  18. package/src/components/Quests/QuestInfo/QuestInfo.tsx +57 -68
  19. package/src/components/Quests/QuestInfo/QuestObjectivesSection.tsx +114 -0
  20. package/src/components/Quests/QuestInfo/QuestRequirementsSection.tsx +74 -0
  21. package/src/components/Quests/QuestInfo/QuestRewardsSection.tsx +128 -0
  22. package/src/components/Quests/QuestInfo/QuestSectionTypes.ts +8 -0
  23. package/src/components/Quests/QuestList.tsx +12 -87
  24. package/src/components/Quests/QuestListRow.tsx +178 -0
  25. package/src/components/Store/CartView.tsx +3 -2
  26. package/src/components/Store/MetadataCollector.tsx +28 -1
  27. package/src/components/Store/__test__/MetadataCollector.spec.tsx +12 -3
  28. package/src/components/Tutorial/TutorialStepper.tsx +2 -0
  29. package/src/components/shared/SimpleTooltip.tsx +32 -19
  30. package/src/index.tsx +1 -1
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpg-engine/long-bow",
3
- "version": "0.8.230",
3
+ "version": "0.8.232",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -20,38 +20,49 @@ export const ItemTooltip: React.FC<IItemTooltipProps> = ({
20
20
  equipmentSet,
21
21
  }) => {
22
22
  const ref = useRef<HTMLDivElement>(null);
23
+ const rafId = useRef<number | null>(null);
23
24
 
24
25
  useEffect(() => {
25
26
  const { current } = ref;
26
27
 
27
28
  if (current) {
28
29
  const handleMouseMove = (event: MouseEvent) => {
30
+ if (rafId.current !== null) return;
31
+
29
32
  const { clientX, clientY } = event;
30
33
 
31
- // Adjust the position of the tooltip based on the mouse position
32
- const rect = current.getBoundingClientRect();
34
+ rafId.current = requestAnimationFrame(() => {
35
+ rafId.current = null;
36
+
37
+ // Adjust the position of the tooltip based on the mouse position
38
+ const rect = current.getBoundingClientRect();
33
39
 
34
- const tooltipWidth = rect.width;
35
- const tooltipHeight = rect.height;
36
- const isOffScreenRight =
37
- clientX + tooltipWidth + offset > window.innerWidth;
38
- const isOffScreenBottom =
39
- clientY + tooltipHeight + offset > window.innerHeight;
40
- const x = isOffScreenRight
41
- ? clientX - tooltipWidth - offset
42
- : clientX + offset;
43
- const y = isOffScreenBottom
44
- ? clientY - tooltipHeight - offset
45
- : clientY + offset;
40
+ const tooltipWidth = rect.width;
41
+ const tooltipHeight = rect.height;
42
+ const isOffScreenRight =
43
+ clientX + tooltipWidth + offset > window.innerWidth;
44
+ const isOffScreenBottom =
45
+ clientY + tooltipHeight + offset > window.innerHeight;
46
+ const x = isOffScreenRight
47
+ ? clientX - tooltipWidth - offset
48
+ : clientX + offset;
49
+ const y = isOffScreenBottom
50
+ ? clientY - tooltipHeight - offset
51
+ : clientY + offset;
46
52
 
47
- current.style.transform = `translate(${x}px, ${y}px)`;
48
- current.style.opacity = '1';
53
+ current.style.transform = `translate(${x}px, ${y}px)`;
54
+ current.style.opacity = '1';
55
+ });
49
56
  };
50
57
 
51
58
  window.addEventListener('mousemove', handleMouseMove);
52
59
 
53
60
  return () => {
54
61
  window.removeEventListener('mousemove', handleMouseMove);
62
+ if (rafId.current !== null) {
63
+ cancelAnimationFrame(rafId.current);
64
+ rafId.current = null;
65
+ }
55
66
  };
56
67
  }
57
68
 
@@ -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, quests[currentIndex]._id);
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
- {quests.length >= 2 ? (
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
- src={quests[currentIndex].thumbnail || thumbnailDefault}
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>{quests[currentIndex].description}</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
- <Button
97
- key={index}
98
- onPointerDown={() =>
99
- button.onClick(
100
- quests[currentIndex]._id,
101
- quests[currentIndex].npcId
102
- )
103
- }
104
- disabled={button.disabled}
105
- buttonType={ButtonTypes.RPGUIButton}
106
- id={`button-${index}`}
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
+ `;
@@ -0,0 +1,8 @@
1
+ export interface IQuestAtlasProps {
2
+ itemsAtlasJSON?: any;
3
+ itemsAtlasIMG?: any;
4
+ entitiesAtlasJSON?: any;
5
+ entitiesAtlasIMG?: any;
6
+ iconsAtlasJSON?: any;
7
+ iconsAtlasIMG?: any;
8
+ }