@rpg-engine/long-bow 0.8.207 → 0.8.208

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.207",
3
+ "version": "0.8.208",
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.109",
87
+ "@rpg-engine/shared": "^0.10.115",
88
88
  "dayjs": "^1.11.2",
89
89
  "font-awesome": "^4.7.0",
90
90
  "fs-extra": "^10.1.0",
@@ -5,6 +5,7 @@ import { FaTimes } from 'react-icons/fa';
5
5
  import styled, { keyframes } from 'styled-components';
6
6
  import ModalPortal from '../Abstractions/ModalPortal';
7
7
  import { ConfirmModal } from '../ConfirmModal';
8
+ import { gemColors } from '../Item/Inventory/ItemGem';
8
9
  import { CTAButton } from '../shared/CTAButton/CTAButton';
9
10
  import { SpriteFromAtlas } from '../shared/SpriteFromAtlas';
10
11
 
@@ -34,6 +35,12 @@ const RARITY_COLORS: Record<string, string> = {
34
35
  const rarityColor = (rarity?: string) =>
35
36
  RARITY_COLORS[(rarity ?? '').toLowerCase()] ?? RARITY_COLORS.common;
36
37
 
38
+ const rarityGlowColor = (rarity?: string): string | null => {
39
+ const key = (rarity ?? '').toLowerCase();
40
+ if (!key || key === 'common') return null;
41
+ return RARITY_COLORS[key] ?? null;
42
+ };
43
+
37
44
  const formatEquipmentSlot = (slot?: string) => {
38
45
  if (!slot) return 'Unknown';
39
46
 
@@ -86,9 +93,9 @@ export const CharacterDetailModal: React.FC<ICharacterDetailModalProps> = ({
86
93
  if (!isOpen || !listing) return null;
87
94
 
88
95
  const snap = listing.characterSnapshot;
89
- const topSkills = Object.entries(snap.skills ?? {})
90
- .sort(([, a], [, b]) => b - a)
91
- .slice(0, 8);
96
+ const topSkills = Object.entries(snap.skills ?? {}).sort(
97
+ ([, a], [, b]) => b - a
98
+ );
92
99
 
93
100
  return (
94
101
  <ModalPortal>
@@ -121,83 +128,101 @@ export const CharacterDetailModal: React.FC<ICharacterDetailModalProps> = ({
121
128
  </CloseButton>
122
129
  </Header>
123
130
 
124
- <HeroSection>
125
- <SpriteContainer>
126
- <SpriteFromAtlas
127
- atlasIMG={characterAtlasIMG}
128
- atlasJSON={characterAtlasJSON}
129
- spriteKey={`${snap.textureKey}/down/standing/0.png`}
130
- imgScale={4}
131
- height={96}
132
- width={96}
133
- centered
134
- />
135
- </SpriteContainer>
136
- <HeroInfo>
137
- <CharacterName>{snap.name || 'Unknown'}</CharacterName>
138
- <CharacterClass>
139
- Lv.{snap.level} · {snap.class}
140
- </CharacterClass>
141
- <CharacterOrigin>
142
- {snap.race} · {snap.faction}
143
- </CharacterOrigin>
144
- <ModeBadge $hardcore={snap.mode?.toLowerCase() === 'hardcore'}>
145
- {snap.mode || 'Standard'}
146
- </ModeBadge>
147
- </HeroInfo>
148
- </HeroSection>
149
-
150
- <Divider />
151
-
152
- <MetaColumns>
153
- {topSkills.length > 0 && (
154
- <Section>
155
- <SectionTitle>Skills</SectionTitle>
156
- <SkillsList>
157
- {topSkills.map(([name, value]) => (
158
- <SkillRow key={name}>
159
- <SkillName>{name}</SkillName>
160
- <SkillValue>{value}</SkillValue>
161
- </SkillRow>
162
- ))}
163
- </SkillsList>
164
- </Section>
165
- )}
166
-
167
- {snap.equipment?.length > 0 && (
168
- <Section>
169
- <SectionTitle>Equipment</SectionTitle>
170
- <EquipmentList>
171
- {snap.equipment.map((eq, i) => (
172
- <EquipmentRow key={i}>
173
- <EquipmentSprite>
174
- <SpriteFromAtlas
175
- atlasIMG={atlasIMG}
176
- atlasJSON={atlasJSON}
177
- spriteKey={eq.itemKey}
178
- imgScale={2}
179
- width={32}
180
- height={32}
181
- centered
182
- />
183
- </EquipmentSprite>
184
- <EquipMeta>
185
- <EquipName>{eq.itemName}</EquipName>
186
- <EquipDetails>
187
- <EquipSlot>{formatEquipmentSlot(eq.slot)}</EquipSlot>
188
- <RarityBadge $rarity={eq.rarity}>
189
- {eq.rarity || 'Common'}
190
- </RarityBadge>
191
- </EquipDetails>
192
- </EquipMeta>
193
- </EquipmentRow>
194
- ))}
195
- </EquipmentList>
196
- </Section>
197
- )}
198
- </MetaColumns>
199
-
200
- <Divider />
131
+ <ScrollableBody>
132
+ <HeroSection>
133
+ <SpriteContainer>
134
+ <SpriteFromAtlas
135
+ atlasIMG={characterAtlasIMG}
136
+ atlasJSON={characterAtlasJSON}
137
+ spriteKey={`${snap.textureKey}/down/standing/0.png`}
138
+ imgScale={4}
139
+ height={96}
140
+ width={96}
141
+ centered
142
+ />
143
+ </SpriteContainer>
144
+ <HeroInfo>
145
+ <CharacterName>{snap.name || 'Unknown'}</CharacterName>
146
+ <CharacterClass>
147
+ Lv.{snap.level} · {snap.class}
148
+ </CharacterClass>
149
+ <CharacterOrigin>
150
+ {snap.race} · {snap.faction}
151
+ </CharacterOrigin>
152
+ <ModeBadge $hardcore={snap.mode?.toLowerCase() === 'hardcore'}>
153
+ {snap.mode || 'Standard'}
154
+ </ModeBadge>
155
+ {snap.gold != null && snap.gold > 0 && (
156
+ <GoldRow>
157
+ <GoldLabel>GOLD</GoldLabel>
158
+ <GoldAmount>{snap.gold.toLocaleString()}</GoldAmount>
159
+ </GoldRow>
160
+ )}
161
+ </HeroInfo>
162
+ </HeroSection>
163
+
164
+ <Divider />
165
+
166
+ <MetaColumns>
167
+ {topSkills.length > 0 && (
168
+ <Section>
169
+ <SectionTitle>Skills</SectionTitle>
170
+ <SkillsList>
171
+ {topSkills.map(([name, value]) => (
172
+ <SkillRow key={name}>
173
+ <SkillName>{name}</SkillName>
174
+ <SkillValue>{value}</SkillValue>
175
+ </SkillRow>
176
+ ))}
177
+ </SkillsList>
178
+ </Section>
179
+ )}
180
+
181
+ {snap.equipment?.length > 0 && (
182
+ <Section>
183
+ <SectionTitle>Equipment</SectionTitle>
184
+ <EquipmentList>
185
+ {snap.equipment.map((eq, i) => (
186
+ <EquipmentRow key={i}>
187
+ <EquipmentSprite $rarity={eq.rarity}>
188
+ <SpriteFromAtlas
189
+ atlasIMG={atlasIMG}
190
+ atlasJSON={atlasJSON}
191
+ spriteKey={eq.itemKey}
192
+ imgScale={2}
193
+ width={32}
194
+ height={32}
195
+ centered
196
+ />
197
+ {eq.attachedGems && eq.attachedGems.length > 0 && (
198
+ <GemRow>
199
+ {eq.attachedGems.map((gem, gi) => (
200
+ <GemDot
201
+ key={gi}
202
+ $color={gemColors[gem.key] ?? '#fff'}
203
+ />
204
+ ))}
205
+ </GemRow>
206
+ )}
207
+ </EquipmentSprite>
208
+ <EquipMeta>
209
+ <EquipName>{eq.itemName}</EquipName>
210
+ <EquipDetails>
211
+ <EquipSlot>{formatEquipmentSlot(eq.slot)}</EquipSlot>
212
+ <RarityBadge $rarity={eq.rarity}>
213
+ {eq.rarity || 'Common'}
214
+ </RarityBadge>
215
+ </EquipDetails>
216
+ </EquipMeta>
217
+ </EquipmentRow>
218
+ ))}
219
+ </EquipmentList>
220
+ </Section>
221
+ )}
222
+ </MetaColumns>
223
+ </ScrollableBody>
224
+
225
+ <FooterDivider />
201
226
 
202
227
  <Footer>
203
228
  <SellerInfo>Listed by {listing.listedByCharacterName}</SellerInfo>
@@ -257,29 +282,16 @@ const ModalContent = styled.div`
257
282
  background: #1a1a2e;
258
283
  border: 2px solid #f59e0b;
259
284
  border-radius: 8px;
260
- padding: 20px 24px;
285
+ padding: 20px 24px 16px;
261
286
  width: 580px;
262
287
  max-width: 96%;
263
288
  max-height: 85dvh;
264
289
  display: flex;
265
290
  flex-direction: column;
266
291
  gap: 14px;
267
- overflow-y: auto;
268
- overflow-x: hidden;
292
+ overflow: hidden;
269
293
  pointer-events: auto;
270
294
  animation: ${scaleIn} 0.15s ease-out;
271
-
272
- &::-webkit-scrollbar {
273
- width: 6px;
274
- }
275
- &::-webkit-scrollbar-track {
276
- background: rgba(0, 0, 0, 0.2);
277
- border-radius: 4px;
278
- }
279
- &::-webkit-scrollbar-thumb {
280
- background: rgba(245, 158, 11, 0.3);
281
- border-radius: 4px;
282
- }
283
295
  `;
284
296
 
285
297
  const Header = styled.div`
@@ -377,6 +389,30 @@ const Divider = styled.hr`
377
389
  flex-shrink: 0;
378
390
  `;
379
391
 
392
+ const FooterDivider = styled(Divider)``;
393
+
394
+ const ScrollableBody = styled.div`
395
+ flex: 1;
396
+ overflow-y: auto;
397
+ overflow-x: hidden;
398
+ display: flex;
399
+ flex-direction: column;
400
+ gap: 14px;
401
+ min-height: 0;
402
+
403
+ &::-webkit-scrollbar {
404
+ width: 6px;
405
+ }
406
+ &::-webkit-scrollbar-track {
407
+ background: rgba(0, 0, 0, 0.2);
408
+ border-radius: 4px;
409
+ }
410
+ &::-webkit-scrollbar-thumb {
411
+ background: rgba(245, 158, 11, 0.3);
412
+ border-radius: 4px;
413
+ }
414
+ `;
415
+
380
416
  const Section = styled.div`
381
417
  display: flex;
382
418
  flex-direction: column;
@@ -445,13 +481,66 @@ const EquipmentRow = styled.div`
445
481
  min-width: 0;
446
482
  `;
447
483
 
448
- const EquipmentSprite = styled.div`
484
+ const EquipmentSprite = styled.div<{ $rarity?: string }>`
485
+ position: relative;
449
486
  display: flex;
450
487
  align-items: center;
451
488
  justify-content: center;
452
489
  width: 32px;
453
490
  height: 32px;
454
491
  flex-shrink: 0;
492
+ border-radius: 3px;
493
+ ${({ $rarity }) => {
494
+ const color = rarityGlowColor($rarity);
495
+ return color
496
+ ? `box-shadow: 0 0 4px 3px ${color} inset, 0 0 6px 2px ${color};`
497
+ : '';
498
+ }}
499
+ `;
500
+
501
+ const GemRow = styled.div`
502
+ position: absolute;
503
+ bottom: -1px;
504
+ left: 0;
505
+ display: flex;
506
+ gap: 1px;
507
+ pointer-events: none;
508
+ `;
509
+
510
+ const GemDot = styled.div<{ $color: string }>`
511
+ width: 5px;
512
+ height: 5px;
513
+ border-radius: 1px;
514
+ transform: rotate(45deg);
515
+ background: radial-gradient(
516
+ circle at 30% 30%,
517
+ rgba(255, 255, 255, 0.8),
518
+ transparent 40%
519
+ ),
520
+ linear-gradient(45deg, ${({ $color }) => $color}, rgba(255, 255, 255, 0.2));
521
+ border: 1px solid rgba(0, 0, 0, 0.6);
522
+ box-shadow: 0 0 3px ${({ $color }) => $color};
523
+ `;
524
+
525
+ const GoldRow = styled.div`
526
+ display: flex;
527
+ align-items: center;
528
+ gap: 4px;
529
+ margin-top: 2px;
530
+ `;
531
+
532
+ const GoldLabel = styled.span`
533
+ font-family: 'Press Start 2P', cursive !important;
534
+ font-size: 0.35rem !important;
535
+ color: #6b7280 !important;
536
+ text-transform: uppercase;
537
+ letter-spacing: 0.5px;
538
+ `;
539
+
540
+ const GoldAmount = styled.span`
541
+ font-family: 'Press Start 2P', cursive !important;
542
+ font-size: 0.38rem !important;
543
+ color: #fde68a !important;
455
544
  `;
456
545
 
457
546
  const EquipMeta = styled.div`