@terrafirmagreg-team/game 0.1.0 → 1.2.1
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 +15 -14
- package/src/components/entry/atlas-sprite.tsx +22 -35
- package/src/components/entry/copy-id-menu.tsx +22 -29
- package/src/components/entry/entry.tsx +99 -20
- package/src/components/entry/group.tsx +27 -46
- package/src/components/entry/icon.tsx +21 -27
- package/src/components/entry/list.tsx +15 -18
- package/src/components/entry/mc-text.tsx +3 -89
- package/src/components/entry/stack-count.tsx +12 -8
- package/src/components/entry/tooltip.tsx +52 -44
- package/src/components/mdx/item-icon.tsx +23 -15
- package/src/components/preview-icon.tsx +2 -1
- package/src/hooks/use-dump-location.ts +4 -2
- package/src/hooks/use-entry-context.ts +3 -1
- package/src/hooks/use-icon-source.ts +22 -13
- package/src/hooks/use-lang-map.ts +4 -2
- package/src/lib/entry/display.ts +2 -1
- package/src/lib/entry/entry.ts +5 -3
- package/src/lib/entry/formula.ts +1 -0
- package/src/lib/entry/href.ts +20 -12
- package/src/lib/entry/icon-load.ts +67 -31
- package/src/lib/entry/icon.ts +3 -2
- package/src/lib/entry/tooltip.ts +12 -10
- package/src/lib/preview.ts +4 -2
- package/src/lib/search.ts +18 -19
- package/src/locales/en_us.json +3 -7
- package/src/locales/ru_ru.json +3 -7
- package/src/locales.ts +1 -0
- package/src/types/entry.ts +41 -2
- package/styles/fonts.css +18 -2
- package/src/components/entry/frame.tsx +0 -32
- package/src/components/entry/icon/tabs.tsx +0 -118
- package/src/lib/dump/assets/atlas/dto.ts +0 -49
- package/src/lib/dump/assets/atlas/lookup.ts +0 -206
- package/src/lib/dump/assets/atlas/sprites.ts +0 -130
- package/src/lib/dump/assets/lang/load.ts +0 -17
- package/src/lib/dump/assets/lang/names.ts +0 -244
- package/src/lib/dump/assets/urls.ts +0 -143
- package/src/lib/dump/client/atlases.ts +0 -83
- package/src/lib/dump/client/dump.ts +0 -188
- package/src/lib/dump/client/groups.ts +0 -31
- package/src/lib/dump/client/keybinds.ts +0 -11
- package/src/lib/dump/client/mods.ts +0 -16
- package/src/lib/dump/client/registry.ts +0 -125
- package/src/lib/dump/client/tabs.ts +0 -27
- package/src/lib/dump/client/tags.ts +0 -64
- package/src/lib/dump/contract/kind.ts +0 -61
- package/src/lib/dump/contract/read.ts +0 -77
- package/src/lib/dump/contract/ref.ts +0 -262
- package/src/lib/dump/contract/text.ts +0 -86
- package/src/lib/dump/data/registry/dto.ts +0 -56
- package/src/lib/dump/data/registry/parse.ts +0 -291
- package/src/lib/dump/data/tab/dto.ts +0 -13
- package/src/lib/dump/data/tab/parse.ts +0 -26
- package/src/lib/dump/data/tag/dto.ts +0 -7
- package/src/lib/dump/data/tag/parse.ts +0 -57
- package/src/lib/dump/gen/data/guides/book.ts +0 -56
- package/src/lib/dump/gen/data/guides/category.ts +0 -30
- package/src/lib/dump/gen/data/guides/entry.ts +0 -48
- package/src/lib/dump/gen/data/models/model.ts +0 -3
- package/src/lib/dump/gen/data/quests/chapter.ts +0 -126
- package/src/lib/dump/gen/data/quests/chapter_groups.ts +0 -11
- package/src/lib/dump/gen/data/quests/data.ts +0 -40
- package/src/lib/dump/gen/data/recipes/recipe.ts +0 -78
- package/src/lib/dump/gen/index.ts +0 -20
- package/src/lib/dump/gen/keybinds.ts +0 -3
- package/src/lib/dump/gen/lang.ts +0 -3
- package/src/lib/dump/gen/manifest.ts +0 -59
- package/src/lib/dump/gen/registry/blocks.ts +0 -60
- package/src/lib/dump/gen/registry/entities.ts +0 -49
- package/src/lib/dump/gen/registry/fluids.ts +0 -48
- package/src/lib/dump/gen/registry/groups.ts +0 -14
- package/src/lib/dump/gen/registry/items.ts +0 -51
- package/src/lib/dump/gen/registry/recipe_categories.ts +0 -64
- package/src/lib/dump/gen/registry/recipes.ts +0 -3
- package/src/lib/dump/gen/registry/tabs.ts +0 -46
- package/src/lib/dump/gen/registry/tags.ts +0 -11
- package/src/lib/dump/json-reader.ts +0 -231
- package/src/lib/dump/manifest/dto.ts +0 -11
- package/src/lib/dump/manifest/parse.ts +0 -167
- package/src/lib/dump/mods/dto.ts +0 -40
- package/src/lib/dump/mods/index.ts +0 -81
- package/src/lib/dump/mods/load.ts +0 -61
- package/src/lib/dump/paths.ts +0 -171
- package/src/lib/dump/reader.ts +0 -15
- package/src/lib/snbt.ts +0 -246
package/package.json
CHANGED
|
@@ -1,25 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@terrafirmagreg-team/game",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "1.2.1",
|
|
4
4
|
"type": "module",
|
|
5
|
+
"description": "Game-data components and catalogs",
|
|
6
|
+
"license": "LGPL-3.0-only",
|
|
5
7
|
"exports": {
|
|
6
8
|
"./components/*": [
|
|
7
9
|
"./src/components/*.tsx",
|
|
8
|
-
"./src/components/*.ts",
|
|
9
10
|
"./src/components/*/index.tsx",
|
|
10
11
|
"./src/components/*/index.ts"
|
|
11
12
|
],
|
|
12
13
|
"./hooks/*": [
|
|
13
14
|
"./src/hooks/*.ts",
|
|
14
|
-
"./src/hooks
|
|
15
|
-
"./src/hooks/*/index.ts",
|
|
16
|
-
"./src/hooks/*/index.tsx"
|
|
15
|
+
"./src/hooks/*/index.ts"
|
|
17
16
|
],
|
|
18
17
|
"./lib/*": [
|
|
19
18
|
"./src/lib/*.ts",
|
|
20
|
-
"./src/lib
|
|
21
|
-
"./src/lib/*/index.ts",
|
|
22
|
-
"./src/lib/*/index.tsx"
|
|
19
|
+
"./src/lib/*/index.ts"
|
|
23
20
|
],
|
|
24
21
|
"./types/*": [
|
|
25
22
|
"./src/types/*.ts",
|
|
@@ -29,12 +26,16 @@
|
|
|
29
26
|
"./locales": "./src/locales.ts"
|
|
30
27
|
},
|
|
31
28
|
"dependencies": {
|
|
32
|
-
"
|
|
33
|
-
"
|
|
34
|
-
"@terrafirmagreg-team/
|
|
35
|
-
"@terrafirmagreg-team/
|
|
36
|
-
"@terrafirmagreg-team/
|
|
37
|
-
"
|
|
29
|
+
"@terrafirmagreg-team/data": "1.2.1",
|
|
30
|
+
"@terrafirmagreg-team/dump": "1.2.1",
|
|
31
|
+
"@terrafirmagreg-team/kit": "1.2.1",
|
|
32
|
+
"@terrafirmagreg-team/search": "1.2.1",
|
|
33
|
+
"@terrafirmagreg-team/sites": "1.2.1",
|
|
34
|
+
"@terrafirmagreg-team/translate": "1.2.1",
|
|
35
|
+
"@terrafirmagreg-team/ui": "1.2.1",
|
|
36
|
+
"fumadocs-core": "16.17.8",
|
|
37
|
+
"lucide-react": "^0.577.0",
|
|
38
|
+
"server-only": "^0.0.1"
|
|
38
39
|
},
|
|
39
40
|
"peerDependencies": {
|
|
40
41
|
"next": ">=15",
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
'use client';
|
|
2
|
+
import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
|
|
2
3
|
|
|
3
|
-
import { atlasPagePublicUrl } from '@terrafirmagreg-team/
|
|
4
|
-
import type { AtlasLookup } from '@terrafirmagreg-team/
|
|
4
|
+
import { atlasPagePublicUrl } from '@terrafirmagreg-team/dump/lib/dump/assets/atlas/lookup';
|
|
5
|
+
import type { AtlasLookup } from '@terrafirmagreg-team/dump/lib/dump/assets/atlas/dto';
|
|
6
|
+
import { Sprite } from '@terrafirmagreg-team/ui/components/blocks/sprite';
|
|
5
7
|
import { Skeleton } from '@terrafirmagreg-team/ui/components/ui/skeleton';
|
|
6
8
|
import { cn } from '@terrafirmagreg-team/ui/lib/utils/cn';
|
|
7
9
|
|
|
@@ -11,7 +13,7 @@ export function AtlasSprite({
|
|
|
11
13
|
size = 16,
|
|
12
14
|
className,
|
|
13
15
|
}: {
|
|
14
|
-
dataset:
|
|
16
|
+
dataset: DatasetRef;
|
|
15
17
|
lookup: AtlasLookup | null;
|
|
16
18
|
size?: number;
|
|
17
19
|
className?: string;
|
|
@@ -19,39 +21,24 @@ export function AtlasSprite({
|
|
|
19
21
|
const url = lookup
|
|
20
22
|
? atlasPagePublicUrl(dataset, lookup.atlas, lookup.page)
|
|
21
23
|
: undefined;
|
|
22
|
-
if (!lookup || !url) {
|
|
23
|
-
return <Skeleton className={cn('inline-block shrink-0', className)} style={{ width: size, height: size }} />;
|
|
24
|
-
}
|
|
25
|
-
const { sprite, page } = lookup;
|
|
26
|
-
const scale = size / Math.max(sprite.w, 1);
|
|
27
|
-
const pos = `-${sprite.x * scale}px -${sprite.y * scale}px`;
|
|
28
|
-
const bgSize = `${page.w * scale}px ${page.h * scale}px`;
|
|
29
|
-
const tint = sprite.tintColor && sprite.tint !== 'none' ? sprite.tintColor : undefined;
|
|
30
24
|
return (
|
|
31
|
-
<
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
WebkitMaskSize: bgSize,
|
|
49
|
-
maskSize: bgSize,
|
|
50
|
-
WebkitMaskRepeat: 'no-repeat',
|
|
51
|
-
maskRepeat: 'no-repeat',
|
|
52
|
-
}
|
|
53
|
-
: {}),
|
|
54
|
-
}}
|
|
25
|
+
<Sprite
|
|
26
|
+
src={url}
|
|
27
|
+
rect={lookup ? lookup.sprite : undefined}
|
|
28
|
+
page={lookup ? lookup.page : undefined}
|
|
29
|
+
tint={
|
|
30
|
+
lookup?.sprite.tintColor && lookup.sprite.tint !== 'none'
|
|
31
|
+
? lookup.sprite.tintColor
|
|
32
|
+
: undefined
|
|
33
|
+
}
|
|
34
|
+
size={size}
|
|
35
|
+
className={className}
|
|
36
|
+
fallback={
|
|
37
|
+
<Skeleton
|
|
38
|
+
className={cn('inline-block shrink-0', className)}
|
|
39
|
+
style={{ width: size, height: size }}
|
|
40
|
+
/>
|
|
41
|
+
}
|
|
55
42
|
/>
|
|
56
43
|
);
|
|
57
44
|
}
|
|
@@ -2,12 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import { Copy } from 'lucide-react';
|
|
4
4
|
import type { ReactNode } from 'react';
|
|
5
|
-
import {
|
|
6
|
-
ContextMenu,
|
|
7
|
-
ContextMenuContent,
|
|
8
|
-
ContextMenuItem,
|
|
9
|
-
ContextMenuTrigger,
|
|
10
|
-
} from '@terrafirmagreg-team/ui/components/ui/context-menu';
|
|
5
|
+
import { CopyMenu } from '@terrafirmagreg-team/ui/components/blocks/nav/copy-menu';
|
|
11
6
|
import { useTranslations } from '@terrafirmagreg-team/translate/hooks/use-translations';
|
|
12
7
|
|
|
13
8
|
export function CopyIdMenu({
|
|
@@ -27,29 +22,27 @@ export function CopyIdMenu({
|
|
|
27
22
|
children: ReactNode;
|
|
28
23
|
}) {
|
|
29
24
|
const t = useTranslations();
|
|
30
|
-
const copy = (value: string) => () => {
|
|
31
|
-
void navigator.clipboard.writeText(value);
|
|
32
|
-
};
|
|
33
25
|
return (
|
|
34
|
-
<
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
26
|
+
<CopyMenu
|
|
27
|
+
items={[
|
|
28
|
+
{
|
|
29
|
+
icon: <Copy />,
|
|
30
|
+
label: label ?? t('Copy id', { note: 'ui.entry.copy' }),
|
|
31
|
+
value: id,
|
|
32
|
+
},
|
|
33
|
+
...(nbt
|
|
34
|
+
? [
|
|
35
|
+
{
|
|
36
|
+
label: t('Copy stack id', { note: 'ui.entry.copy_stack' }),
|
|
37
|
+
value: `${id}${nbt}`,
|
|
38
|
+
},
|
|
39
|
+
{ label: t('Copy NBT', { note: 'ui.entry.copy_nbt' }), value: nbt },
|
|
40
|
+
]
|
|
41
|
+
: []),
|
|
42
|
+
]}
|
|
43
|
+
extra={extra}
|
|
44
|
+
>
|
|
45
|
+
{children}
|
|
46
|
+
</CopyMenu>
|
|
54
47
|
);
|
|
55
48
|
}
|
|
@@ -1,12 +1,16 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import Link from 'next/link';
|
|
4
|
+
import { usePathname } from 'next/navigation';
|
|
5
|
+
import { parseSectionLocation } from '@terrafirmagreg-team/sites/location';
|
|
4
6
|
import type { ReactElement } from 'react';
|
|
5
7
|
import { EntryIcon } from '@terrafirmagreg-team/game/components/entry/icon';
|
|
6
8
|
import { EntryTooltip } from '@terrafirmagreg-team/game/components/entry/tooltip';
|
|
7
9
|
import { CopyIdMenu } from '@terrafirmagreg-team/game/components/entry/copy-id-menu';
|
|
8
10
|
import { useEntryContext } from '@terrafirmagreg-team/game/hooks/use-entry-context';
|
|
9
11
|
import { useLangMap } from '@terrafirmagreg-team/game/hooks/use-lang-map';
|
|
12
|
+
import { ResolvedLangContext } from '@terrafirmagreg-team/translate/hooks/use-resolved-lang';
|
|
13
|
+
import { useContext, useMemo } from 'react';
|
|
10
14
|
import {
|
|
11
15
|
Tooltip,
|
|
12
16
|
TooltipContent,
|
|
@@ -17,9 +21,13 @@ import {
|
|
|
17
21
|
isIconKind,
|
|
18
22
|
isTagKind,
|
|
19
23
|
refId,
|
|
20
|
-
} from '@terrafirmagreg-team/
|
|
21
|
-
import { formatNbt } from '@terrafirmagreg-team/
|
|
22
|
-
import {
|
|
24
|
+
} from '@terrafirmagreg-team/dump/lib/dump/contract/ref';
|
|
25
|
+
import { formatNbt } from '@terrafirmagreg-team/dump/lib/snbt';
|
|
26
|
+
import { fetchEntityModel } from '@terrafirmagreg-team/game/lib/entry/icon-load';
|
|
27
|
+
import { useDumpLocation } from '@terrafirmagreg-team/game/hooks/use-dump-location';
|
|
28
|
+
import { createDumpAssetResolver } from '@terrafirmagreg-team/dump/lib/dump/assets/urls';
|
|
29
|
+
import { GlbViewer } from '@terrafirmagreg-team/ui/components/blocks/media/glb/viewer';
|
|
30
|
+
import { stackIdFromRef } from '@terrafirmagreg-team/dump/lib/dump/contract/ref';
|
|
23
31
|
import { stackDisplay } from '@terrafirmagreg-team/game/lib/entry/display';
|
|
24
32
|
import { loadStackDisplay } from '@terrafirmagreg-team/game/lib/entry/tooltip';
|
|
25
33
|
import { useAsyncResult } from '@terrafirmagreg-team/ui/hooks/use-async-value';
|
|
@@ -29,7 +37,16 @@ import {
|
|
|
29
37
|
usesStackTooltip,
|
|
30
38
|
} from '@terrafirmagreg-team/game/lib/entry/entry';
|
|
31
39
|
import { cn } from '@terrafirmagreg-team/ui/lib/utils/cn';
|
|
32
|
-
import type {
|
|
40
|
+
import type {
|
|
41
|
+
BlockDirectiveProps,
|
|
42
|
+
EntityDirectiveProps,
|
|
43
|
+
EntryProps,
|
|
44
|
+
FluidDirectiveProps,
|
|
45
|
+
ItemDirectiveProps,
|
|
46
|
+
KindEntryProps,
|
|
47
|
+
RecipeDirectiveProps,
|
|
48
|
+
TagDirectiveProps,
|
|
49
|
+
} from '@terrafirmagreg-team/game/types/entry';
|
|
33
50
|
|
|
34
51
|
export function Entry({
|
|
35
52
|
reference: referenceProp,
|
|
@@ -51,11 +68,17 @@ export function Entry({
|
|
|
51
68
|
missing,
|
|
52
69
|
tooltip,
|
|
53
70
|
children,
|
|
71
|
+
entityView,
|
|
72
|
+
icon: iconProp,
|
|
54
73
|
...hrefOpts
|
|
55
74
|
}: EntryProps) {
|
|
56
75
|
const ctx = useEntryContext();
|
|
57
|
-
const
|
|
58
|
-
const
|
|
76
|
+
const pathname = usePathname();
|
|
77
|
+
const locId = parseSectionLocation(pathname).dataset;
|
|
78
|
+
const dataset =
|
|
79
|
+
datasetProp ?? ctx?.dataset ?? (locId ? { id: locId } : undefined);
|
|
80
|
+
const resolvedLang = useContext(ResolvedLangContext)?.lang;
|
|
81
|
+
const lang = langProp ?? ctx?.lang ?? resolvedLang;
|
|
59
82
|
const fetchedLangMap = useLangMap(
|
|
60
83
|
dataset,
|
|
61
84
|
lang,
|
|
@@ -77,7 +100,7 @@ export function Entry({
|
|
|
77
100
|
refKind === 'block' ||
|
|
78
101
|
refKind === 'fluid' ||
|
|
79
102
|
refKind === 'entity')
|
|
80
|
-
? `${dataset}:${stackId}`
|
|
103
|
+
? `${dataset.id}/${dataset.version ?? ''}:${stackId}`
|
|
81
104
|
: undefined;
|
|
82
105
|
const displayPeek =
|
|
83
106
|
displayKey && dataset
|
|
@@ -144,9 +167,15 @@ export function Entry({
|
|
|
144
167
|
|
|
145
168
|
const icon = (
|
|
146
169
|
<EntryIcon
|
|
147
|
-
reference={reference}
|
|
148
|
-
id={refIdStr}
|
|
149
|
-
kind={
|
|
170
|
+
reference={iconProp ?? reference}
|
|
171
|
+
id={iconProp ? refId(iconProp) : refIdStr}
|
|
172
|
+
kind={
|
|
173
|
+
iconProp
|
|
174
|
+
? isIconKind(iconProp.kind)
|
|
175
|
+
? iconProp.kind
|
|
176
|
+
: undefined
|
|
177
|
+
: iconKind
|
|
178
|
+
}
|
|
150
179
|
tag={tag}
|
|
151
180
|
dataset={dataset}
|
|
152
181
|
size={size}
|
|
@@ -155,6 +184,7 @@ export function Entry({
|
|
|
155
184
|
missing={missing}
|
|
156
185
|
chip={variant === 'chip'}
|
|
157
186
|
preferVariant={preferVariant}
|
|
187
|
+
entityView={entityView}
|
|
158
188
|
badge={badge}
|
|
159
189
|
badgeClassName={badgeClassName}
|
|
160
190
|
>
|
|
@@ -212,18 +242,67 @@ export function Entry({
|
|
|
212
242
|
);
|
|
213
243
|
}
|
|
214
244
|
|
|
215
|
-
function kindEntry(kind: EntryProps['kind']) {
|
|
216
|
-
function KindEntry(props:
|
|
245
|
+
function kindEntry<P extends KindEntryProps>(kind: EntryProps['kind']) {
|
|
246
|
+
function KindEntry(props: P) {
|
|
217
247
|
return <Entry kind={kind} {...props} />;
|
|
218
248
|
}
|
|
219
249
|
return KindEntry;
|
|
220
250
|
}
|
|
221
251
|
|
|
222
|
-
export const ItemEntry = kindEntry('item');
|
|
223
|
-
export const BlockEntry = kindEntry('block');
|
|
224
|
-
export const FluidEntry = kindEntry('fluid');
|
|
225
|
-
export
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
252
|
+
export const ItemEntry = kindEntry<ItemDirectiveProps>('item');
|
|
253
|
+
export const BlockEntry = kindEntry<BlockDirectiveProps>('block');
|
|
254
|
+
export const FluidEntry = kindEntry<FluidDirectiveProps>('fluid');
|
|
255
|
+
export function EntityEntry({
|
|
256
|
+
view = 'auto',
|
|
257
|
+
id,
|
|
258
|
+
dataset: datasetProp,
|
|
259
|
+
name,
|
|
260
|
+
...rest
|
|
261
|
+
}: EntityDirectiveProps) {
|
|
262
|
+
const ctx = useEntryContext();
|
|
263
|
+
const pathname = usePathname();
|
|
264
|
+
const locId = parseSectionLocation(pathname).dataset;
|
|
265
|
+
const dataset = useMemo(
|
|
266
|
+
() =>
|
|
267
|
+
datasetProp ?? ctx?.dataset ?? (locId ? { id: locId } : undefined),
|
|
268
|
+
[datasetProp, ctx, locId],
|
|
269
|
+
);
|
|
270
|
+
const modelKey = dataset && id ? `${dataset.id}/${dataset.version ?? ''}:${id}` : undefined;
|
|
271
|
+
const modelRes = useAsyncResult(
|
|
272
|
+
view === 'model' && modelKey
|
|
273
|
+
? async () => ({
|
|
274
|
+
key: modelKey,
|
|
275
|
+
url: await fetchEntityModel(dataset!, id!),
|
|
276
|
+
})
|
|
277
|
+
: false,
|
|
278
|
+
[view, dataset, id, modelKey],
|
|
279
|
+
);
|
|
280
|
+
useDumpLocation(dataset);
|
|
281
|
+
const resolveResource = useMemo(
|
|
282
|
+
() => (dataset ? createDumpAssetResolver(dataset) : undefined),
|
|
283
|
+
[dataset],
|
|
284
|
+
);
|
|
285
|
+
if (view === 'model') {
|
|
286
|
+
const model = modelRes.value;
|
|
287
|
+
const src = model && model.key === modelKey ? model.url : undefined;
|
|
288
|
+
if (src === undefined) return null;
|
|
289
|
+
return src ? (
|
|
290
|
+
<GlbViewer src={src} title={name ?? id} resolveResource={resolveResource} />
|
|
291
|
+
) : (
|
|
292
|
+
<Entry kind="entity" id={id} dataset={dataset} name={name} {...rest} />
|
|
293
|
+
);
|
|
294
|
+
}
|
|
295
|
+
return (
|
|
296
|
+
<Entry
|
|
297
|
+
kind="entity"
|
|
298
|
+
id={id}
|
|
299
|
+
dataset={dataset}
|
|
300
|
+
name={name}
|
|
301
|
+
entityView={view}
|
|
302
|
+
{...rest}
|
|
303
|
+
/>
|
|
304
|
+
);
|
|
305
|
+
}
|
|
306
|
+
export const TagEntry = kindEntry<TagDirectiveProps>('itemTag');
|
|
307
|
+
export const RecipeEntry = kindEntry<RecipeDirectiveProps>('recipe');
|
|
308
|
+
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
'use client';
|
|
2
|
+
import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
|
|
2
3
|
|
|
3
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
SPRITE_CLUSTER_INSET,
|
|
6
|
+
SPRITE_CLUSTER_UNITS,
|
|
7
|
+
SpriteCluster,
|
|
8
|
+
} from '@terrafirmagreg-team/ui/components/blocks/sprite';
|
|
4
9
|
import { EntryIcon, entrySizePx, ENTRY_CYCLE_MS } from '@terrafirmagreg-team/game/components/entry/icon';
|
|
5
10
|
import { useCycleIndex } from '@terrafirmagreg-team/ui/hooks/use-cycle-index';
|
|
6
11
|
import { useAsyncResult } from '@terrafirmagreg-team/ui/hooks/use-async-value';
|
|
@@ -12,26 +17,11 @@ import {
|
|
|
12
17
|
tagKindOf,
|
|
13
18
|
type ReferenceKind,
|
|
14
19
|
type TaggedReference,
|
|
15
|
-
} from '@terrafirmagreg-team/
|
|
20
|
+
} from '@terrafirmagreg-team/dump/lib/dump/contract/ref';
|
|
16
21
|
import type { EntrySize } from '@terrafirmagreg-team/game/types/entry';
|
|
17
22
|
|
|
18
|
-
const GAME_SLOT = 18;
|
|
19
23
|
const GAME_ICON = 12.8;
|
|
20
24
|
const STACK_SCALE = 1.15;
|
|
21
|
-
const FRAME_INSET = 6;
|
|
22
|
-
const POSITIONS: readonly (readonly [number, number])[][] = [
|
|
23
|
-
[],
|
|
24
|
-
[[1.6, 1.6]],
|
|
25
|
-
[
|
|
26
|
-
[0.4, 0.8],
|
|
27
|
-
[2.8, 2.4],
|
|
28
|
-
],
|
|
29
|
-
[
|
|
30
|
-
[-0.8, 0],
|
|
31
|
-
[1.6, 1.6],
|
|
32
|
-
[4.0, 3.2],
|
|
33
|
-
],
|
|
34
|
-
];
|
|
35
25
|
|
|
36
26
|
function memberRef(
|
|
37
27
|
member: string | TaggedReference,
|
|
@@ -47,7 +37,7 @@ function memberKey(member: string | TaggedReference, kind: ReferenceKind): strin
|
|
|
47
37
|
|
|
48
38
|
async function expandTagRefs(
|
|
49
39
|
refs: readonly TaggedReference[],
|
|
50
|
-
dataset:
|
|
40
|
+
dataset: DatasetRef,
|
|
51
41
|
): Promise<TaggedReference[]> {
|
|
52
42
|
const groups = await Promise.all(
|
|
53
43
|
refs.map((ref) =>
|
|
@@ -90,7 +80,7 @@ export function EntryGroup({
|
|
|
90
80
|
kind?: ReferenceKind;
|
|
91
81
|
size?: EntrySize;
|
|
92
82
|
chip?: boolean;
|
|
93
|
-
dataset?:
|
|
83
|
+
dataset?: DatasetRef;
|
|
94
84
|
limit?: number;
|
|
95
85
|
cycle?: number;
|
|
96
86
|
missing?: 'placeholder' | 'item' | 'hide';
|
|
@@ -103,7 +93,7 @@ export function EntryGroup({
|
|
|
103
93
|
cycle !== undefined &&
|
|
104
94
|
dataset &&
|
|
105
95
|
references.some((ref) => isTagKind(ref.kind))
|
|
106
|
-
? `${dataset}|${references
|
|
96
|
+
? `${dataset.id}/${dataset.version ?? ''}|${references
|
|
107
97
|
.map((ref) => `${ref.kind}:${stackIdFromRef(ref)}`)
|
|
108
98
|
.join('|')}`
|
|
109
99
|
: null;
|
|
@@ -149,32 +139,23 @@ export function EntryGroup({
|
|
|
149
139
|
</span>
|
|
150
140
|
);
|
|
151
141
|
}
|
|
152
|
-
const
|
|
153
|
-
|
|
154
|
-
const icons = members.slice(0, Math.min(limit, POSITIONS.length - 1));
|
|
155
|
-
const positions = POSITIONS[icons.length];
|
|
142
|
+
const scale =
|
|
143
|
+
(sizePx - (chip ? SPRITE_CLUSTER_INSET : 0)) / SPRITE_CLUSTER_UNITS;
|
|
156
144
|
return (
|
|
157
|
-
<
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
dataset={dataset}
|
|
172
|
-
size={GAME_ICON * STACK_SCALE * scale}
|
|
173
|
-
missing={missing}
|
|
174
|
-
/>
|
|
175
|
-
</span>
|
|
176
|
-
))}
|
|
177
|
-
</span>
|
|
178
|
-
</EntryFrame>
|
|
145
|
+
<SpriteCluster
|
|
146
|
+
size={sizePx}
|
|
147
|
+
chip={chip}
|
|
148
|
+
limit={limit}
|
|
149
|
+
items={members.slice(0, limit).map((member) => (
|
|
150
|
+
<EntryIcon
|
|
151
|
+
key={memberKey(member, kind)}
|
|
152
|
+
reference={memberRef(member, kind)}
|
|
153
|
+
dataset={dataset}
|
|
154
|
+
size={GAME_ICON * STACK_SCALE * scale}
|
|
155
|
+
missing={missing}
|
|
156
|
+
/>
|
|
157
|
+
))}
|
|
158
|
+
/>
|
|
179
159
|
);
|
|
180
160
|
}
|
|
161
|
+
|
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
'use client';
|
|
2
|
+
import type { DatasetRef } from '@terrafirmagreg-team/sites/location';
|
|
2
3
|
|
|
3
4
|
import { useState } from 'react';
|
|
4
5
|
import { useEntryContext } from '@terrafirmagreg-team/game/hooks/use-entry-context';
|
|
5
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
Sprite,
|
|
8
|
+
SpriteFrame,
|
|
9
|
+
} from '@terrafirmagreg-team/ui/components/blocks/sprite';
|
|
6
10
|
import { AtlasSprite } from '@terrafirmagreg-team/game/components/entry/atlas-sprite';
|
|
7
11
|
import { useIconSource } from '@terrafirmagreg-team/game/hooks/use-icon-source';
|
|
8
12
|
import { Skeleton } from '@terrafirmagreg-team/ui/components/ui/skeleton';
|
|
9
|
-
import { cn } from '@terrafirmagreg-team/ui/lib/utils/cn';
|
|
10
13
|
import type {
|
|
11
14
|
DumpNbt,
|
|
12
15
|
ReferenceKind,
|
|
13
16
|
TaggedReference,
|
|
14
|
-
} from '@terrafirmagreg-team/
|
|
17
|
+
} from '@terrafirmagreg-team/dump/lib/dump/contract/ref';
|
|
15
18
|
import type { EntrySize } from '@terrafirmagreg-team/game/types/entry';
|
|
16
19
|
|
|
17
20
|
export { ENTRY_CYCLE_MS } from '@terrafirmagreg-team/game/hooks/use-icon-source';
|
|
@@ -34,6 +37,7 @@ export function EntryIcon({
|
|
|
34
37
|
missing = 'placeholder',
|
|
35
38
|
chip = false,
|
|
36
39
|
preferVariant = false,
|
|
40
|
+
entityView,
|
|
37
41
|
badge,
|
|
38
42
|
badgeClassName,
|
|
39
43
|
children,
|
|
@@ -43,7 +47,7 @@ export function EntryIcon({
|
|
|
43
47
|
id?: string;
|
|
44
48
|
kind?: ReferenceKind;
|
|
45
49
|
tag?: DumpNbt;
|
|
46
|
-
dataset?:
|
|
50
|
+
dataset?: DatasetRef;
|
|
47
51
|
size?: EntrySize;
|
|
48
52
|
labeled?: boolean;
|
|
49
53
|
label?: string;
|
|
@@ -51,6 +55,7 @@ export function EntryIcon({
|
|
|
51
55
|
chip?: boolean;
|
|
52
56
|
|
|
53
57
|
preferVariant?: boolean;
|
|
58
|
+
entityView?: 'auto' | 'texture' | 'egg';
|
|
54
59
|
badge?: React.ReactNode;
|
|
55
60
|
badgeClassName?: string;
|
|
56
61
|
children?: React.ReactNode;
|
|
@@ -66,6 +71,7 @@ export function EntryIcon({
|
|
|
66
71
|
tag,
|
|
67
72
|
missing,
|
|
68
73
|
preferVariant,
|
|
74
|
+
entityView,
|
|
69
75
|
});
|
|
70
76
|
const caption = label || undefined;
|
|
71
77
|
const name = caption ?? source.rawId;
|
|
@@ -95,16 +101,10 @@ export function EntryIcon({
|
|
|
95
101
|
</span>
|
|
96
102
|
);
|
|
97
103
|
const img = (src: string) => (
|
|
98
|
-
|
|
99
|
-
<img
|
|
104
|
+
<Sprite
|
|
100
105
|
src={src}
|
|
101
106
|
alt={name}
|
|
102
|
-
|
|
103
|
-
height={size}
|
|
104
|
-
className="rounded-sm"
|
|
105
|
-
style={{ width: size, height: size, imageRendering: 'pixelated' }}
|
|
106
|
-
loading="lazy"
|
|
107
|
-
decoding="async"
|
|
107
|
+
size={size}
|
|
108
108
|
onError={() =>
|
|
109
109
|
setFileFailed({ key: imgSrc ?? source.sourceKey, failed: true })
|
|
110
110
|
}
|
|
@@ -121,12 +121,14 @@ export function EntryIcon({
|
|
|
121
121
|
missing={missing}
|
|
122
122
|
/>
|
|
123
123
|
);
|
|
124
|
+
} else if (source.isEntity && imgSrc && !failed) {
|
|
125
|
+
icon = img(imgSrc);
|
|
126
|
+
} else if (source.isEntity && source.awaitingTexture) {
|
|
127
|
+
icon = hideMissing ? null : pendingBox;
|
|
124
128
|
} else if (source.lookup && dataset) {
|
|
125
129
|
icon = <AtlasSprite dataset={dataset} lookup={source.lookup} size={size} />;
|
|
126
130
|
} else if (source.pending || source.awaitingAtlas) {
|
|
127
131
|
icon = hideMissing ? null : pendingBox;
|
|
128
|
-
} else if (source.isEntity && imgSrc && !failed) {
|
|
129
|
-
icon = img(imgSrc);
|
|
130
132
|
} else if (source.awaitingTexture) {
|
|
131
133
|
icon = hideMissing ? null : pendingBox;
|
|
132
134
|
} else if (source.usesAtlas || !imgSrc || failed) {
|
|
@@ -150,23 +152,14 @@ export function EntryIcon({
|
|
|
150
152
|
if (!icon && !labeled && !children) return null;
|
|
151
153
|
|
|
152
154
|
const framed = (
|
|
153
|
-
<
|
|
155
|
+
<SpriteFrame
|
|
154
156
|
size={size}
|
|
155
157
|
chip={chip}
|
|
156
|
-
|
|
158
|
+
badge={badge}
|
|
159
|
+
badgeClassName={badgeClassName}
|
|
157
160
|
>
|
|
158
161
|
{icon}
|
|
159
|
-
|
|
160
|
-
<span
|
|
161
|
-
className={cn(
|
|
162
|
-
'pointer-events-none absolute -right-0.5 -bottom-0.5 z-10',
|
|
163
|
-
badgeClassName,
|
|
164
|
-
)}
|
|
165
|
-
>
|
|
166
|
-
{badge}
|
|
167
|
-
</span>
|
|
168
|
-
) : null}
|
|
169
|
-
</EntryFrame>
|
|
162
|
+
</SpriteFrame>
|
|
170
163
|
);
|
|
171
164
|
|
|
172
165
|
if (!labeled && !children) return framed;
|
|
@@ -185,3 +178,4 @@ export function EntryIcon({
|
|
|
185
178
|
</span>
|
|
186
179
|
);
|
|
187
180
|
}
|
|
181
|
+
|