@rpg-engine/long-bow 0.8.35 → 0.8.36

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpg-engine/long-bow",
3
- "version": "0.8.35",
3
+ "version": "0.8.36",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -84,7 +84,7 @@
84
84
  "dependencies": {
85
85
  "@capacitor/core": "^6.1.0",
86
86
  "@rollup/plugin-image": "^2.1.1",
87
- "@rpg-engine/shared": "^0.10.0",
87
+ "@rpg-engine/shared": "^0.9.104",
88
88
  "dayjs": "^1.11.2",
89
89
  "font-awesome": "^4.7.0",
90
90
  "fs-extra": "^10.1.0",
@@ -6,7 +6,12 @@ import {
6
6
  IInformationCenterItem,
7
7
  IInformationCenterNPC,
8
8
  IVideoGuide,
9
+ isMobileOrTablet,
9
10
  } from '@rpg-engine/shared';
11
+ import {
12
+ UI_BREAKPOINT_MOBILE,
13
+ UI_BREAKPOINT_SMALL_LAPTOP,
14
+ } from '../../constants/uiBreakpoints';
10
15
  import { DraggableContainer } from '../DraggableContainer';
11
16
  import { InternalTabs } from '../InternalTabs/InternalTabs';
12
17
  import { RPGUIContainerTypes } from '../RPGUI/RPGUIContainer';
@@ -45,6 +50,7 @@ export const InformationCenter: React.FC<IInformationCenterProps> = ({
45
50
  initialSearchQuery = '',
46
51
  }) => {
47
52
  const [activeTab, setActiveTab] = useState('bestiary');
53
+ const isMobile = isMobileOrTablet();
48
54
 
49
55
  if (loading) {
50
56
  return <LoadingMessage>Loading...</LoadingMessage>;
@@ -103,6 +109,8 @@ export const InformationCenter: React.FC<IInformationCenterProps> = ({
103
109
  <DraggableContainer
104
110
  title="Information Center"
105
111
  type={RPGUIContainerTypes.Framed}
112
+ width={isMobile ? '95%' : '80%'}
113
+ minWidth="300px"
106
114
  >
107
115
  <Container>
108
116
  <InternalTabs
@@ -122,9 +130,32 @@ export const InformationCenter: React.FC<IInformationCenterProps> = ({
122
130
 
123
131
  const Container = styled.div`
124
132
  width: 100%;
125
- max-width: 800px;
133
+ max-width: 100%;
126
134
  margin: 0 auto;
127
- padding: 1rem;
135
+ padding: 0.125rem;
136
+ overflow: hidden;
137
+ box-sizing: border-box;
138
+
139
+ @media (min-width: 320px) {
140
+ padding: 0.25rem;
141
+ }
142
+
143
+ @media (min-width: 360px) {
144
+ padding: 0.5rem;
145
+ }
146
+
147
+ @media (min-width: 480px) {
148
+ padding: 0.75rem;
149
+ }
150
+
151
+ @media (min-width: ${UI_BREAKPOINT_MOBILE}) {
152
+ max-width: 900px;
153
+ padding: 1rem;
154
+ }
155
+
156
+ @media (min-width: ${UI_BREAKPOINT_SMALL_LAPTOP}) {
157
+ max-width: 1200px;
158
+ }
128
159
  `;
129
160
 
130
161
  const LoadingMessage = styled.div`
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import styled from 'styled-components';
3
+ import { UI_BREAKPOINT_MOBILE } from '../../constants/uiBreakpoints';
3
4
  import { SpriteFromAtlas } from '../shared/SpriteFromAtlas';
4
5
 
5
6
  interface IInformationCenterCellProps {
@@ -43,7 +44,9 @@ export const InformationCenterCell: React.FC<IInformationCenterCellProps> = ({
43
44
  imgScale={1}
44
45
  />
45
46
  </SpriteContainer>
46
- <CellName>{name}</CellName>
47
+ <CellNameContainer>
48
+ <CellName>{name}</CellName>
49
+ </CellNameContainer>
47
50
  </CellContainer>
48
51
  );
49
52
  };
@@ -51,19 +54,31 @@ export const InformationCenterCell: React.FC<IInformationCenterCellProps> = ({
51
54
  const CellContainer = styled.div`
52
55
  position: relative;
53
56
  background: rgba(0, 0, 0, 0.2);
54
- padding: 0.75rem;
57
+ padding: 0.25rem;
55
58
  border-radius: 4px;
56
59
  display: flex;
57
60
  flex-direction: column;
58
61
  align-items: center;
59
- justify-content: center;
62
+ justify-content: flex-start;
60
63
  cursor: pointer;
61
64
  transition: background-color 0.2s ease;
62
65
  width: 100%;
63
66
  height: 100%;
64
- min-width: 120px;
65
- min-height: 120px;
67
+ min-height: 135px;
66
68
  box-sizing: border-box;
69
+ gap: 0.25rem;
70
+ margin: 0;
71
+
72
+ @media (min-width: 360px) {
73
+ padding: 0.75rem;
74
+ gap: 0.75rem;
75
+ margin: 2px;
76
+ width: calc(100% - 4px);
77
+ }
78
+
79
+ @media (min-width: ${UI_BREAKPOINT_MOBILE}) {
80
+ min-height: 150px;
81
+ }
67
82
 
68
83
  &:hover {
69
84
  background: rgba(0, 0, 0, 0.3);
@@ -71,17 +86,32 @@ const CellContainer = styled.div`
71
86
  `;
72
87
 
73
88
  const SpriteContainer = styled.div`
74
- margin-bottom: 0.5rem;
89
+ margin-bottom: 0;
75
90
  display: flex;
76
91
  justify-content: center;
77
92
  align-items: center;
78
- width: 64px;
79
- height: 64px;
93
+ width: 40px;
94
+ height: 40px;
80
95
  position: relative;
81
96
  background: rgba(0, 0, 0, 0.3);
82
97
  border-radius: 4px;
83
98
  flex-shrink: 0;
84
99
 
100
+ @media (min-width: 360px) {
101
+ width: 48px;
102
+ height: 48px;
103
+ }
104
+
105
+ @media (min-width: 480px) {
106
+ width: 64px;
107
+ height: 64px;
108
+ }
109
+
110
+ @media (min-width: ${UI_BREAKPOINT_MOBILE}) {
111
+ width: 72px;
112
+ height: 72px;
113
+ }
114
+
85
115
  .sprite-from-atlas-img {
86
116
  position: absolute;
87
117
  top: 50%;
@@ -91,8 +121,18 @@ const SpriteContainer = styled.div`
91
121
  }
92
122
  `;
93
123
 
124
+ const CellNameContainer = styled.div`
125
+ display: flex;
126
+ flex-direction: column;
127
+ align-items: center;
128
+ justify-content: flex-start;
129
+ flex: 1;
130
+ width: 100%;
131
+ padding-top: 0.25rem;
132
+ `;
133
+
94
134
  const CellName = styled.h3`
95
- font-size: 0.7rem;
135
+ font-size: 0.55rem;
96
136
  color: #fef08a;
97
137
  margin: 0;
98
138
  text-align: center;
@@ -100,4 +140,25 @@ const CellName = styled.h3`
100
140
  line-height: 1.2;
101
141
  word-break: break-word;
102
142
  max-width: 100%;
143
+ overflow-wrap: break-word;
144
+ hyphens: auto;
145
+ width: 100%;
146
+ padding: 0 0.125rem;
147
+ box-sizing: border-box;
148
+
149
+ @media (min-width: 320px) {
150
+ padding: 0 0.25rem;
151
+ }
152
+
153
+ @media (min-width: 360px) {
154
+ font-size: 0.6rem;
155
+ }
156
+
157
+ @media (min-width: 480px) {
158
+ font-size: 0.7rem;
159
+ }
160
+
161
+ @media (min-width: ${UI_BREAKPOINT_MOBILE}) {
162
+ font-size: 0.8rem;
163
+ }
103
164
  `;
@@ -201,7 +201,7 @@ export const InformationCenterBestiarySection = ({
201
201
  selectedSubtype,
202
202
  selectedAttackType,
203
203
  ]}
204
- itemHeight="180px"
204
+ itemHeight={isMobile ? '150px' : '180px'}
205
205
  />
206
206
  {!isMobile && tooltipState && tooltipState.item && (
207
207
  <Portal>
@@ -153,7 +153,7 @@ export const InformationCenterItemsSection: React.FC<IItemsSectionProps> = ({
153
153
  dependencies={[selectedItemCategory, selectedTier]}
154
154
  tabId={tabId}
155
155
  layout="grid"
156
- itemHeight="180px"
156
+ itemHeight={isMobile ? '150px' : '180px'}
157
157
  />
158
158
  {!isMobile && tooltipState && tooltipState.item && (
159
159
  <Portal>
@@ -2,6 +2,7 @@ import {
2
2
  IVideoGuide,
3
3
  VideoGuideCategory,
4
4
  VideoGuideLanguage,
5
+ isMobileOrTablet,
5
6
  } from '@rpg-engine/shared';
6
7
  import React, { useMemo, useState } from 'react';
7
8
  import styled from 'styled-components';
@@ -26,6 +27,7 @@ export const InformationCenterTutorialsSection: React.FC<ITutorialsSectionProps>
26
27
  initialSearchQuery,
27
28
  tabId,
28
29
  }) => {
30
+ const isMobile = isMobileOrTablet();
29
31
  const [searchQuery, setSearchQuery] = useState(initialSearchQuery);
30
32
  const [selectedCategory, setSelectedCategory] = useState<string>('all');
31
33
  const [, setCurrentPage] = useState(1);
@@ -81,7 +83,7 @@ export const InformationCenterTutorialsSection: React.FC<ITutorialsSectionProps>
81
83
  </Ellipsis>
82
84
  </GuideTitle>
83
85
  <GuideDescription>
84
- <Ellipsis maxWidth="100%" maxLines={5}>
86
+ <Ellipsis maxWidth="100%" maxLines={isMobile ? 3 : 5}>
85
87
  {guide.description}
86
88
  </Ellipsis>
87
89
  </GuideDescription>
@@ -145,9 +147,9 @@ export const InformationCenterTutorialsSection: React.FC<ITutorialsSectionProps>
145
147
  dependencies={[selectedCategory]}
146
148
  tabId={tabId}
147
149
  layout="grid"
148
- gridColumns={GRID_COLUMNS}
150
+ gridColumns={isMobile ? 1 : GRID_COLUMNS}
149
151
  itemsPerPage={ITEMS_PER_PAGE}
150
- itemHeight="320px"
152
+ itemHeight={isMobile ? '280px' : '320px'}
151
153
  />
152
154
  );
153
155
  };
@@ -1,5 +1,6 @@
1
1
  import React from 'react';
2
2
  import styled from 'styled-components';
3
+ import { UI_BREAKPOINT_MOBILE } from '../../constants/uiBreakpoints';
3
4
 
4
5
  interface TabItem {
5
6
  id: string;
@@ -73,14 +74,27 @@ const TabButton = styled.button<{
73
74
  hoverColor: string;
74
75
  }>`
75
76
  flex: 1;
76
- padding: 0.5rem 1rem;
77
- font-size: 0.875rem;
77
+ padding: 0.25rem 0.5rem;
78
+ font-size: 0.75rem;
78
79
  font-weight: 500;
79
80
  border-right: 1px solid ${props => props.borderColor};
80
81
  background-color: ${props =>
81
82
  props.active ? props.activeColor : 'transparent'};
82
83
  color: ${props =>
83
84
  props.active ? props.activeTextColor : props.inactiveColor};
85
+ white-space: nowrap;
86
+ overflow: hidden;
87
+ text-overflow: ellipsis;
88
+
89
+ @media (min-width: 480px) {
90
+ padding: 0.375rem 0.75rem;
91
+ font-size: 0.8125rem;
92
+ }
93
+
94
+ @media (min-width: ${UI_BREAKPOINT_MOBILE}) {
95
+ padding: 0.5rem 1rem;
96
+ font-size: 0.875rem;
97
+ }
84
98
 
85
99
  &:last-child {
86
100
  border-right: none;
@@ -92,4 +106,6 @@ const TabButton = styled.button<{
92
106
  }
93
107
  `;
94
108
 
95
- const ContentWrapper = styled.div``;
109
+ const ContentWrapper = styled.div`
110
+ width: 100%;
111
+ `;
@@ -1,4 +1,4 @@
1
- import { IItem } from '@rpg-engine/shared';
1
+ import { IItem, ItemQualityLevel } from '@rpg-engine/shared';
2
2
  import React from 'react';
3
3
  import styled from 'styled-components';
4
4
  import { uiColors } from '../../../constants/uiColors';
@@ -6,6 +6,7 @@ import { uiFonts } from '../../../constants/uiFonts';
6
6
  import { SpriteFromAtlas } from '../../shared/SpriteFromAtlas';
7
7
  import { ErrorBoundary } from '../Inventory/ErrorBoundary';
8
8
  import { EquipmentSlotSpriteByType } from '../Inventory/ItemSlot';
9
+ import { qualityColorHex } from '../Inventory/ItemSlotQuality';
9
10
  import { rarityColor } from '../Inventory/ItemSlotRarity';
10
11
 
11
12
  interface IItemInfoProps {
@@ -191,8 +192,24 @@ const Container = styled.div<{ item: IItem }>`
191
192
  padding: 0.5rem;
192
193
  font-size: ${uiFonts.size.small};
193
194
  border: 3px solid ${({ item }) => rarityColor(item) ?? uiColors.lightGray};
195
+ box-shadow: ${({ item }) => `0 0 5px 2px ${rarityColor(item)}`};
194
196
  height: max-content;
195
197
  width: 18rem;
198
+ position: relative;
199
+
200
+ ${({ item }) =>
201
+ item?.quality && item.quality !== ItemQualityLevel.Normal &&
202
+ `
203
+ &::before {
204
+ content: '★';
205
+ position: absolute;
206
+ top: 0.2rem;
207
+ left: 0.5rem;
208
+ font-size: 1.2rem;
209
+ color: ${qualityColorHex(item)};
210
+ text-shadow: 0 0 3px black;
211
+ }
212
+ `}
196
213
 
197
214
  @media (max-width: 640px) {
198
215
  width: 80vw;
@@ -6,7 +6,7 @@ import {
6
6
  ItemContainerType,
7
7
  ItemSlotType,
8
8
  ItemSubType,
9
- ItemType,
9
+ ItemType
10
10
  } from '@rpg-engine/shared';
11
11
 
12
12
  import { observer } from 'mobx-react-lite';
@@ -15,10 +15,11 @@ import Draggable, { DraggableEventHandler } from 'react-draggable';
15
15
  import styled from 'styled-components';
16
16
  import useTouchTarget from '../../../hooks/useTouchTarget';
17
17
  import { IPosition } from '../../../types/eventTypes';
18
- import { rarityColor } from './ItemSlotRarity';
19
- import { ItemSlotRenderer } from './ItemSlotRenderer';
20
18
  import { useItemSlotDetails } from './context/ItemSlotDetailsContext';
21
19
  import { useItemSlotDragging } from './context/ItemSlotDraggingContext';
20
+ import { qualityColorHex } from './ItemSlotQuality';
21
+ import { rarityColor } from './ItemSlotRarity';
22
+ import { ItemSlotRenderer } from './ItemSlotRenderer';
22
23
 
23
24
  export const EquipmentSlotSpriteByType: any = {
24
25
  Neck: 'accessories/corruption-necklace.png',
@@ -539,6 +540,17 @@ const Container = styled.div<ContainerTypes>`
539
540
  ${({ item }) => `0 0 4px 3px ${rarityColor(item)}`};
540
541
  }
541
542
 
543
+ .quality-star {
544
+ position: absolute;
545
+ top: 0.5rem;
546
+ left: 0.5rem;
547
+ font-size: 1.2rem;
548
+ z-index: 2;
549
+ text-shadow: 0 0 3px black;
550
+ pointer-events: none;
551
+ color: ${({ item }) => qualityColorHex(item)};
552
+ }
553
+
542
554
  &::before {
543
555
  content: '';
544
556
  position: absolute;
@@ -0,0 +1,18 @@
1
+ import { IItem, ItemQualityLevel } from '@rpg-engine/shared';
2
+
3
+ export const qualityColorHex = (item: IItem | null) => {
4
+ switch (item?.quality) {
5
+ case ItemQualityLevel.HighQuality:
6
+ return '#00bfff';
7
+ case ItemQualityLevel.Exceptional:
8
+ return '#ff8c00';
9
+ case ItemQualityLevel.Mastercrafted:
10
+ return '#ff00ff';
11
+ case ItemQualityLevel.Ancient:
12
+ return '#ffd700';
13
+ case ItemQualityLevel.Mythic:
14
+ return '#ff0080';
15
+ default:
16
+ return 'transparent';
17
+ }
18
+ };
@@ -1,8 +1,9 @@
1
1
  import {
2
+ getItemTextureKeyPath,
2
3
  IItem,
3
4
  ItemContainerType,
5
+ ItemQualityLevel,
4
6
  ItemSlotType,
5
- getItemTextureKeyPath,
6
7
  } from '@rpg-engine/shared';
7
8
  import React from 'react';
8
9
  import { v4 as uuidv4 } from 'uuid';
@@ -46,6 +47,9 @@ export const ItemSlotRenderer: React.FC<IProps> = ({
46
47
 
47
48
  return (
48
49
  <ErrorBoundary key={item._id}>
50
+ {item.quality && item.quality !== ItemQualityLevel.Normal && (
51
+ <div className="quality-star">★</div>
52
+ )}
49
53
  <SpriteFromAtlas
50
54
  atlasIMG={atlasIMG}
51
55
  atlasJSON={atlasJSON}
@@ -101,5 +105,5 @@ export const ItemSlotRenderer: React.FC<IProps> = ({
101
105
  }
102
106
  };
103
107
 
104
- return <>{onRenderSlot(item)}</>;
108
+ return onRenderSlot(item);
105
109
  };