@rpg-engine/long-bow 0.8.206 → 0.8.207

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.206",
3
+ "version": "0.8.207",
4
4
  "license": "MIT",
5
5
  "main": "dist/index.js",
6
6
  "typings": "dist/index.d.ts",
@@ -15,6 +15,8 @@ export interface ICharacterListingModalProps {
15
15
  isOpen: boolean;
16
16
  onClose: () => void;
17
17
  accountCharacters: ICharacter[];
18
+ /** ID of the currently active/playing character — cannot be listed */
19
+ activeCharacterId?: string;
18
20
  /** Items atlas — for UI sprites like the DC coin */
19
21
  atlasJSON: any;
20
22
  atlasIMG: any;
@@ -30,6 +32,7 @@ export const CharacterListingModal: React.FC<ICharacterListingModalProps> = ({
30
32
  isOpen,
31
33
  onClose,
32
34
  accountCharacters,
35
+ activeCharacterId,
33
36
  atlasJSON,
34
37
  atlasIMG,
35
38
  characterAtlasJSON,
@@ -55,7 +58,9 @@ export const CharacterListingModal: React.FC<ICharacterListingModalProps> = ({
55
58
  c => !c.isListedForSale && !c.tradedAt
56
59
  );
57
60
 
58
- const canList = !!selectedId && Number(price) > 0;
61
+ const isActiveCharacter = (c: ICharacter) => !!activeCharacterId && c._id === activeCharacterId;
62
+
63
+ const canList = !!selectedId && Number(price) > 0 && selectedId !== activeCharacterId;
59
64
 
60
65
  const handleClose = () => {
61
66
  setSelectedId(null);
@@ -110,22 +115,25 @@ export const CharacterListingModal: React.FC<ICharacterListingModalProps> = ({
110
115
  ) : (
111
116
  eligibleCharacters.map(character => {
112
117
  const isSelected = selectedId === character._id;
118
+ const isActive = isActiveCharacter(character);
113
119
  return (
114
120
  <CharacterRow
115
121
  key={character._id}
116
- $selected={isSelected}
117
- onPointerDown={() => setSelectedId(character._id!)}
122
+ $selected={isSelected && !isActive}
123
+ $disabled={isActive}
124
+ onPointerDown={() => !isActive && setSelectedId(character._id!)}
118
125
  role="radio"
119
- aria-checked={isSelected}
120
- tabIndex={0}
126
+ aria-checked={isSelected && !isActive}
127
+ aria-disabled={isActive}
128
+ tabIndex={isActive ? -1 : 0}
121
129
  onKeyDown={e => {
122
- if (e.key === 'Enter' || e.key === ' ') {
130
+ if (!isActive && (e.key === 'Enter' || e.key === ' ')) {
123
131
  e.preventDefault();
124
132
  setSelectedId(character._id!);
125
133
  }
126
134
  }}
127
135
  >
128
- <RadioCircle $selected={isSelected} />
136
+ <RadioCircle $selected={isSelected && !isActive} $disabled={isActive} />
129
137
  <SpriteWrapper>
130
138
  <SpriteFromAtlas
131
139
  atlasIMG={characterAtlasIMG}
@@ -140,6 +148,7 @@ export const CharacterListingModal: React.FC<ICharacterListingModalProps> = ({
140
148
  <CharacterName>{character.name || 'Unknown'}</CharacterName>
141
149
  <CharacterMeta>Level {getLevel(character)}</CharacterMeta>
142
150
  </CharacterInfo>
151
+ {isActive && <ActiveTag>In-game</ActiveTag>}
143
152
  </CharacterRow>
144
153
  );
145
154
  })
@@ -266,20 +275,21 @@ const CharacterList = styled.div`
266
275
  &::-webkit-scrollbar-thumb { background: rgba(245,158,11,0.3); border-radius: 4px; }
267
276
  `;
268
277
 
269
- const CharacterRow = styled.div<{ $selected: boolean }>`
278
+ const CharacterRow = styled.div<{ $selected: boolean; $disabled?: boolean }>`
270
279
  display: flex;
271
280
  align-items: center;
272
281
  gap: 12px;
273
282
  padding: 10px 12px;
274
- border: 1px solid ${({ $selected }) => ($selected ? '#f59e0b' : 'rgba(255,255,255,0.08)')};
283
+ border: 1px solid ${({ $selected, $disabled }) => ($disabled ? 'rgba(255,255,255,0.04)' : $selected ? '#f59e0b' : 'rgba(255,255,255,0.08)')};
275
284
  border-radius: 6px;
276
- background: ${({ $selected }) => ($selected ? 'rgba(245,158,11,0.1)' : 'rgba(255,255,255,0.02)')};
277
- cursor: pointer;
285
+ background: ${({ $selected, $disabled }) => ($disabled ? 'rgba(0,0,0,0.15)' : $selected ? 'rgba(245,158,11,0.1)' : 'rgba(255,255,255,0.02)')};
286
+ cursor: ${({ $disabled }) => ($disabled ? 'not-allowed' : 'pointer')};
287
+ opacity: ${({ $disabled }) => ($disabled ? 0.5 : 1)};
278
288
  transition: border-color 0.15s, background 0.15s;
279
289
 
280
290
  &:hover {
281
- border-color: ${({ $selected }) => ($selected ? '#f59e0b' : 'rgba(245,158,11,0.4)')};
282
- background: ${({ $selected }) => ($selected ? 'rgba(245,158,11,0.1)' : 'rgba(245,158,11,0.05)')};
291
+ border-color: ${({ $selected, $disabled }) => ($disabled ? 'rgba(255,255,255,0.04)' : $selected ? '#f59e0b' : 'rgba(245,158,11,0.4)')};
292
+ background: ${({ $selected, $disabled }) => ($disabled ? 'rgba(0,0,0,0.15)' : $selected ? 'rgba(245,158,11,0.1)' : 'rgba(245,158,11,0.05)')};
283
293
  }
284
294
 
285
295
  &:focus-visible {
@@ -288,11 +298,11 @@ const CharacterRow = styled.div<{ $selected: boolean }>`
288
298
  }
289
299
  `;
290
300
 
291
- const RadioCircle = styled.div<{ $selected: boolean }>`
301
+ const RadioCircle = styled.div<{ $selected: boolean; $disabled?: boolean }>`
292
302
  width: 14px;
293
303
  height: 14px;
294
304
  border-radius: 50%;
295
- border: 2px solid ${({ $selected }) => ($selected ? '#f59e0b' : 'rgba(255,255,255,0.4)')};
305
+ border: 2px solid ${({ $selected, $disabled }) => ($disabled ? 'rgba(255,255,255,0.15)' : $selected ? '#f59e0b' : 'rgba(255,255,255,0.4)')};
296
306
  flex-shrink: 0;
297
307
  display: flex;
298
308
  align-items: center;
@@ -303,12 +313,23 @@ const RadioCircle = styled.div<{ $selected: boolean }>`
303
313
  width: 6px;
304
314
  height: 6px;
305
315
  border-radius: 50%;
306
- background: #f59e0b;
316
+ background: ${({ $disabled }) => ($disabled ? 'rgba(255,255,255,0.15)' : '#f59e0b')};
307
317
  opacity: ${({ $selected }) => ($selected ? 1 : 0)};
308
318
  transition: opacity 0.15s;
309
319
  }
310
320
  `;
311
321
 
322
+ const ActiveTag = styled.span`
323
+ font-family: 'Press Start 2P', cursive !important;
324
+ font-size: 0.35rem !important;
325
+ color: #666 !important;
326
+ background: rgba(255,255,255,0.06);
327
+ padding: 3px 6px;
328
+ border-radius: 3px;
329
+ white-space: nowrap;
330
+ margin-left: auto;
331
+ `;
332
+
312
333
  const SpriteWrapper = styled.div`
313
334
  display: flex;
314
335
  align-items: center;
@@ -336,6 +336,7 @@ export const Marketplace: React.FC<IMarketPlaceProps> = props => {
336
336
  onCharacterDelist={onCharacterDelist ?? (() => {})}
337
337
  accountCharacters={accountCharacters ?? []}
338
338
  onCharacterList={onCharacterList ?? (() => {})}
339
+ activeCharacterId={props.characterId}
339
340
  atlasJSON={props.atlasJSON}
340
341
  atlasIMG={props.atlasIMG}
341
342
  characterAtlasJSON={characterAtlasJSON ?? props.atlasJSON}
@@ -20,6 +20,8 @@ export interface IMyCharacterListingsPanelProps {
20
20
  /** Characters that can be listed (used for the List a Character modal) */
21
21
  accountCharacters: ICharacter[];
22
22
  onCharacterList: (characterId: string, price: number) => void;
23
+ /** ID of the currently active character */
24
+ activeCharacterId?: string;
23
25
  /** Items atlas — for UI sprites like the DC coin */
24
26
  atlasJSON: any;
25
27
  atlasIMG: any;
@@ -39,6 +41,7 @@ export const MyCharacterListingsPanel: React.FC<IMyCharacterListingsPanelProps>
39
41
  onCharacterDelist,
40
42
  accountCharacters,
41
43
  onCharacterList,
44
+ activeCharacterId,
42
45
  atlasJSON,
43
46
  atlasIMG,
44
47
  characterAtlasJSON,
@@ -80,6 +83,7 @@ export const MyCharacterListingsPanel: React.FC<IMyCharacterListingsPanelProps>
80
83
  isOpen={isListingModalOpen}
81
84
  onClose={() => setIsListingModalOpen(false)}
82
85
  accountCharacters={accountCharacters}
86
+ activeCharacterId={activeCharacterId}
83
87
  atlasJSON={atlasJSON}
84
88
  atlasIMG={atlasIMG}
85
89
  characterAtlasJSON={characterAtlasJSON}