@rpg-engine/long-bow 0.7.97 → 0.7.98

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 (53) hide show
  1. package/dist/components/DPad/JoystickDPad.d.ts +21 -0
  2. package/dist/components/InformationCenter/InformationCenter.d.ts +29 -0
  3. package/dist/components/InformationCenter/InformationCenterCell.d.ts +14 -0
  4. package/dist/components/InformationCenter/InformationCenterTabView.d.ts +19 -0
  5. package/dist/components/InformationCenter/InformationCenterTypes.d.ts +79 -0
  6. package/dist/components/InformationCenter/sections/bestiary/BestiarySection.d.ts +12 -0
  7. package/dist/components/InformationCenter/sections/bestiary/InformationCenterNPCDetails.d.ts +12 -0
  8. package/dist/components/InformationCenter/sections/bestiary/InformationCenterNPCTooltip.d.ts +9 -0
  9. package/dist/components/InformationCenter/sections/faq/FaqSection.d.ts +8 -0
  10. package/dist/components/InformationCenter/sections/items/InformationCenterItemDetails.d.ts +11 -0
  11. package/dist/components/InformationCenter/sections/items/InformationCenterItemTooltip.d.ts +7 -0
  12. package/dist/components/InformationCenter/sections/items/ItemsSection.d.ts +11 -0
  13. package/dist/components/InformationCenter/sections/tutorials/TutorialsSection.d.ts +8 -0
  14. package/dist/components/InformationCenter/shared/BaseInformationDetails.d.ts +10 -0
  15. package/dist/components/shared/BaseTooltip.d.ts +12 -0
  16. package/dist/components/shared/Collapsible/Collapsible.d.ts +9 -0
  17. package/dist/components/shared/Portal/Portal.d.ts +6 -0
  18. package/dist/index.d.ts +1 -0
  19. package/dist/long-bow.cjs.development.js +243 -5
  20. package/dist/long-bow.cjs.development.js.map +1 -1
  21. package/dist/long-bow.cjs.production.min.js +1 -1
  22. package/dist/long-bow.cjs.production.min.js.map +1 -1
  23. package/dist/long-bow.esm.js +244 -7
  24. package/dist/long-bow.esm.js.map +1 -1
  25. package/dist/mocks/informationCenter.mocks.d.ts +6 -0
  26. package/dist/stories/Features/craftbook/CraftBook.stories.d.ts +2 -0
  27. package/dist/stories/UI/info/InformationCenter.stories.d.ts +7 -0
  28. package/dist/stories/UI/joystick/JoystickDPad.stories.d.ts +6 -0
  29. package/package.json +1 -1
  30. package/src/components/CraftBook/CraftBook.tsx +70 -31
  31. package/src/components/DPad/JoystickDPad.tsx +318 -0
  32. package/src/components/InformationCenter/InformationCenter.tsx +155 -0
  33. package/src/components/InformationCenter/InformationCenterCell.tsx +96 -0
  34. package/src/components/InformationCenter/InformationCenterTabView.tsx +121 -0
  35. package/src/components/InformationCenter/InformationCenterTypes.ts +87 -0
  36. package/src/components/InformationCenter/sections/bestiary/BestiarySection.tsx +170 -0
  37. package/src/components/InformationCenter/sections/bestiary/InformationCenterNPCDetails.tsx +366 -0
  38. package/src/components/InformationCenter/sections/bestiary/InformationCenterNPCTooltip.tsx +204 -0
  39. package/src/components/InformationCenter/sections/faq/FaqSection.tsx +71 -0
  40. package/src/components/InformationCenter/sections/items/InformationCenterItemDetails.tsx +323 -0
  41. package/src/components/InformationCenter/sections/items/InformationCenterItemTooltip.tsx +88 -0
  42. package/src/components/InformationCenter/sections/items/ItemsSection.tsx +180 -0
  43. package/src/components/InformationCenter/sections/tutorials/TutorialsSection.tsx +144 -0
  44. package/src/components/InformationCenter/shared/BaseInformationDetails.tsx +162 -0
  45. package/src/components/InternalTabs/InternalTabs.tsx +1 -3
  46. package/src/components/shared/BaseTooltip.tsx +60 -0
  47. package/src/components/shared/Collapsible/Collapsible.tsx +70 -0
  48. package/src/components/shared/Portal/Portal.tsx +19 -0
  49. package/src/index.tsx +1 -0
  50. package/src/mocks/informationCenter.mocks.ts +562 -0
  51. package/src/stories/Features/craftbook/CraftBook.stories.tsx +15 -1
  52. package/src/stories/UI/info/InformationCenter.stories.tsx +58 -0
  53. package/src/stories/UI/joystick/JoystickDPad.stories.tsx +52 -0
@@ -0,0 +1,96 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+ import { SpriteFromAtlas } from '../shared/SpriteFromAtlas';
4
+
5
+ interface IInformationCenterCellProps {
6
+ name: string;
7
+ spriteKey: string;
8
+ atlasJSON: any;
9
+ atlasIMG: string;
10
+ onClick?: () => void;
11
+ onMouseEnter?: (e: React.MouseEvent) => void;
12
+ onMouseLeave?: () => void;
13
+ onMouseMove?: (e: React.MouseEvent) => void;
14
+ onTouchStart?: (e: React.TouchEvent) => void;
15
+ }
16
+
17
+ export const InformationCenterCell: React.FC<IInformationCenterCellProps> = ({
18
+ name,
19
+ spriteKey,
20
+ atlasJSON,
21
+ atlasIMG,
22
+ onClick,
23
+ onMouseEnter,
24
+ onMouseLeave,
25
+ onMouseMove,
26
+ onTouchStart,
27
+ }) => {
28
+ return (
29
+ <CellContainer
30
+ onClick={onClick}
31
+ onMouseEnter={onMouseEnter}
32
+ onMouseLeave={onMouseLeave}
33
+ onMouseMove={onMouseMove}
34
+ onTouchStart={onTouchStart}
35
+ >
36
+ <SpriteContainer>
37
+ <SpriteFromAtlas
38
+ atlasJSON={atlasJSON}
39
+ atlasIMG={atlasIMG}
40
+ spriteKey={spriteKey}
41
+ width={32}
42
+ height={32}
43
+ imgScale={1}
44
+ />
45
+ </SpriteContainer>
46
+ <CellName>{name}</CellName>
47
+ </CellContainer>
48
+ );
49
+ };
50
+
51
+ const CellContainer = styled.div`
52
+ position: relative;
53
+ background: rgba(0, 0, 0, 0.2);
54
+ padding: 0.75rem;
55
+ border-radius: 4px;
56
+ display: flex;
57
+ flex-direction: column;
58
+ align-items: center;
59
+ cursor: pointer;
60
+ transition: background-color 0.2s ease;
61
+ min-height: 120px;
62
+
63
+ &:hover {
64
+ background: rgba(0, 0, 0, 0.3);
65
+ }
66
+ `;
67
+
68
+ const SpriteContainer = styled.div`
69
+ margin-bottom: 0.5rem;
70
+ display: flex;
71
+ justify-content: center;
72
+ align-items: center;
73
+ width: 64px;
74
+ height: 64px;
75
+ position: relative;
76
+ background: rgba(0, 0, 0, 0.3);
77
+ border-radius: 4px;
78
+
79
+ .sprite-from-atlas-img {
80
+ position: absolute;
81
+ top: 50%;
82
+ left: 50%;
83
+ transform: translate(-50%, -50%) scale(2);
84
+ image-rendering: pixelated;
85
+ }
86
+ `;
87
+
88
+ const CellName = styled.h3`
89
+ font-size: 0.7rem;
90
+ color: #fef08a;
91
+ margin: 0;
92
+ text-align: center;
93
+ font-family: 'Press Start 2P', cursive;
94
+ line-height: 1.2;
95
+ word-break: break-word;
96
+ `;
@@ -0,0 +1,121 @@
1
+ import React from 'react';
2
+ import styled from 'styled-components';
3
+ import { usePagination } from '../CraftBook/hooks/usePagination';
4
+ import { Dropdown, IOptionsProps } from '../Dropdown';
5
+ import { Pagination } from '../shared/Pagination/Pagination';
6
+ import { SearchBar } from '../shared/SearchBar/SearchBar';
7
+
8
+ interface IInformationCenterTabViewProps<T> {
9
+ items: T[];
10
+ searchQuery: string;
11
+ onSearchChange: (query: string) => void;
12
+ filterItems: (items: T[]) => T[];
13
+ renderContent: (items: T[]) => React.ReactNode;
14
+ searchPlaceholder: string;
15
+ filterOptions?: {
16
+ options: IOptionsProps[];
17
+ selectedOption: string;
18
+ onOptionChange: (value: string) => void;
19
+ };
20
+ emptyMessage?: string;
21
+ dependencies?: any[];
22
+ }
23
+
24
+ const ITEMS_PER_PAGE = 5;
25
+
26
+ export function InformationCenterTabView<T>({
27
+ items,
28
+ searchQuery,
29
+ onSearchChange,
30
+ filterItems,
31
+ renderContent,
32
+ searchPlaceholder,
33
+ filterOptions,
34
+ emptyMessage = 'No items found',
35
+ dependencies = [],
36
+ }: IInformationCenterTabViewProps<T>): React.ReactElement {
37
+ const filteredItems = filterItems(items);
38
+
39
+ const {
40
+ currentPage,
41
+ setCurrentPage,
42
+ paginatedItems,
43
+ totalPages,
44
+ } = usePagination({
45
+ items: filteredItems,
46
+ itemsPerPage: ITEMS_PER_PAGE,
47
+ dependencies: [searchQuery, ...dependencies],
48
+ });
49
+
50
+ return (
51
+ <Section>
52
+ <HeaderContainer>
53
+ <StyledSearchBar
54
+ value={searchQuery}
55
+ onChange={onSearchChange}
56
+ placeholder={searchPlaceholder}
57
+ />
58
+ {filterOptions && (
59
+ <StyledDropdown
60
+ options={filterOptions.options}
61
+ onChange={filterOptions.onOptionChange}
62
+ width="200px"
63
+ />
64
+ )}
65
+ </HeaderContainer>
66
+
67
+ {paginatedItems.length === 0 ? (
68
+ <EmptyMessage>{emptyMessage}</EmptyMessage>
69
+ ) : (
70
+ <>
71
+ {renderContent(paginatedItems)}
72
+ <StyledPagination
73
+ currentPage={currentPage}
74
+ totalPages={totalPages}
75
+ onPageChange={setCurrentPage}
76
+ />
77
+ </>
78
+ )}
79
+ </Section>
80
+ );
81
+ }
82
+
83
+ const Section = styled.div`
84
+ display: flex;
85
+ flex-direction: column;
86
+ gap: 1rem;
87
+ height: 100%;
88
+ min-height: 400px;
89
+ overflow-y: auto;
90
+ `;
91
+
92
+ const HeaderContainer = styled.div`
93
+ display: flex;
94
+ justify-content: space-between;
95
+ align-items: flex-start;
96
+ gap: 1rem;
97
+ padding: 0 1rem;
98
+ margin-top: 1.5rem;
99
+ `;
100
+
101
+ const StyledSearchBar = styled(SearchBar)`
102
+ margin-bottom: 1rem;
103
+ `;
104
+
105
+ const StyledDropdown = styled(Dropdown)`
106
+ min-width: 150px;
107
+ `;
108
+
109
+ const StyledPagination = styled(Pagination)`
110
+ margin-top: 1rem;
111
+ `;
112
+
113
+ const EmptyMessage = styled.div`
114
+ text-align: center;
115
+ color: #9ca3af;
116
+ padding: 2rem;
117
+ flex: 1;
118
+ display: flex;
119
+ align-items: center;
120
+ justify-content: center;
121
+ `;
@@ -0,0 +1,87 @@
1
+ import {
2
+ EntityAttackType,
3
+ ICharacterPermanentBuff,
4
+ IEquippableItemBlueprint,
5
+ INPCLoot,
6
+ ItemRarities,
7
+ NPCAlignment,
8
+ NPCSubtype,
9
+ RangeTypes,
10
+ } from '@rpg-engine/shared';
11
+
12
+ export enum MovementSpeed {
13
+ ExtraSlow = 1.5,
14
+ Slow = 2.25,
15
+ Standard = 2.6,
16
+ Fast = 3,
17
+ ExtraFast = 3.5,
18
+ }
19
+
20
+ export enum EntityEffectBlueprint {
21
+ Poison = 'poison',
22
+ Bleeding = 'bleeding',
23
+ Freezing = 'freezing',
24
+ Burning = 'burning',
25
+ Corruption = 'corruption',
26
+ VineGrasp = 'vine-grasp',
27
+ Curse = 'curse',
28
+ Drain = 'drain',
29
+ Shadow = 'shadow',
30
+ Stun = 'stun',
31
+ Knockback = 'knockback',
32
+ Rage = 'rage',
33
+ Weakness = 'weakness',
34
+ Cripple = 'cripple',
35
+ Regeneration = 'regeneration',
36
+ }
37
+
38
+ export enum LootProbability {
39
+ VeryRare = 0.5,
40
+ Rare = 1,
41
+ Uncommon = 10,
42
+ SemiCommon = 15,
43
+ Common = 20,
44
+ VeryCommon = 35,
45
+ }
46
+
47
+ interface INPCBlueprintSpellArea {
48
+ spellKey: string;
49
+ probability: number;
50
+ power: string;
51
+ }
52
+
53
+ export interface IInformationCenterNPC {
54
+ id: string;
55
+ name: string;
56
+ key: string;
57
+ subType: NPCSubtype;
58
+ alignment: NPCAlignment;
59
+ attackType: EntityAttackType;
60
+ maxRangeAttack: RangeTypes;
61
+ speed: MovementSpeed;
62
+ baseHealth: number;
63
+ skills: {
64
+ level: number;
65
+ strength?: { level: number };
66
+ dexterity?: { level: number };
67
+ resistance?: { level: number };
68
+ };
69
+ fleeOnLowHealth: boolean;
70
+ entityEffects: EntityEffectBlueprint[];
71
+ areaSpells: INPCBlueprintSpellArea[];
72
+ loots: INPCLoot[];
73
+ }
74
+
75
+ export interface IInformationCenterItem extends IEquippableItemBlueprint {
76
+ tier: number;
77
+ rarity: ItemRarities;
78
+ rangeType?: EntityAttackType;
79
+ basePrice: number;
80
+ canSell: boolean;
81
+ maxStackSize: number;
82
+ usableEffectDescription?: string;
83
+ entityEffects?: [EntityEffectBlueprint];
84
+ entityEffectChance?: number;
85
+ equippedBuff?: ICharacterPermanentBuff[];
86
+ equippedBuffDescription?: string;
87
+ }
@@ -0,0 +1,170 @@
1
+ import React, { useState } from 'react';
2
+ import styled from 'styled-components';
3
+ import { Portal } from '../../../shared/Portal/Portal';
4
+ import { InformationCenterCell } from '../../InformationCenterCell';
5
+ import { InformationCenterTabView } from '../../InformationCenterTabView';
6
+ import { IInformationCenterNPC } from '../../InformationCenterTypes';
7
+ import { InformationCenterNPCDetails } from './InformationCenterNPCDetails';
8
+ import { InformationCenterNPCTooltip } from './InformationCenterNPCTooltip';
9
+
10
+ interface IBestiarySectionProps {
11
+ bestiaryItems: IInformationCenterNPC[];
12
+ itemsAtlasJSON: Record<string, any>;
13
+ itemsAtlasIMG: string;
14
+ entitiesAtlasJSON: Record<string, any>;
15
+ entitiesAtlasIMG: string;
16
+ initialSearchQuery: string;
17
+ }
18
+
19
+ export const BestiarySection: React.FC<IBestiarySectionProps> = ({
20
+ bestiaryItems,
21
+ itemsAtlasJSON,
22
+ itemsAtlasIMG,
23
+ entitiesAtlasJSON,
24
+ entitiesAtlasIMG,
25
+ initialSearchQuery,
26
+ }) => {
27
+ const [bestiarySearchQuery, setBestiarySearchQuery] = useState(
28
+ initialSearchQuery
29
+ );
30
+ const [tooltipData, setTooltipData] = useState<{
31
+ npc: IInformationCenterNPC;
32
+ position: { x: number; y: number };
33
+ } | null>(null);
34
+ const [
35
+ selectedMonster,
36
+ setSelectedMonster,
37
+ ] = useState<IInformationCenterNPC | null>(null);
38
+ const [isTouchDevice] = useState('ontouchstart' in window);
39
+
40
+ const filterItems = (items: IInformationCenterNPC[]) => {
41
+ return items.filter(item =>
42
+ item.name.toLowerCase().includes(bestiarySearchQuery.toLowerCase())
43
+ );
44
+ };
45
+
46
+ const handleMouseEnter = (
47
+ monster: IInformationCenterNPC,
48
+ event: React.MouseEvent
49
+ ) => {
50
+ if (!isTouchDevice && !selectedMonster) {
51
+ setTooltipData({
52
+ npc: monster,
53
+ position: { x: event.clientX, y: event.clientY },
54
+ });
55
+ }
56
+ };
57
+
58
+ const handleMouseLeave = () => {
59
+ if (!isTouchDevice) {
60
+ setTooltipData(null);
61
+ }
62
+ };
63
+
64
+ const handleMouseMove = (event: React.MouseEvent) => {
65
+ if (!isTouchDevice && tooltipData) {
66
+ setTooltipData({
67
+ ...tooltipData,
68
+ position: { x: event.clientX, y: event.clientY },
69
+ });
70
+ }
71
+ };
72
+
73
+ const handleTouchStart = (
74
+ monster: IInformationCenterNPC,
75
+ event: React.TouchEvent
76
+ ) => {
77
+ if (isTouchDevice) {
78
+ event.preventDefault();
79
+ const touch = event.touches[0];
80
+ if (tooltipData?.npc.id === monster.id) {
81
+ setTooltipData(null);
82
+ } else {
83
+ setTooltipData({
84
+ npc: monster,
85
+ position: { x: touch.clientX, y: touch.clientY },
86
+ });
87
+ }
88
+ }
89
+ };
90
+
91
+ const handleMonsterClick = (monster: IInformationCenterNPC) => {
92
+ setSelectedMonster(monster);
93
+ setTooltipData(null);
94
+ };
95
+
96
+ const renderContent = (items: IInformationCenterNPC[]) => (
97
+ <BestiaryGrid>
98
+ {items.map(item => (
99
+ <InformationCenterCell
100
+ key={item.id}
101
+ name={item.name}
102
+ spriteKey={item.key}
103
+ atlasJSON={entitiesAtlasJSON}
104
+ atlasIMG={entitiesAtlasIMG}
105
+ onClick={() => handleMonsterClick(item)}
106
+ onMouseEnter={e => handleMouseEnter(item, e)}
107
+ onMouseLeave={handleMouseLeave}
108
+ onMouseMove={handleMouseMove}
109
+ onTouchStart={e => handleTouchStart(item, e)}
110
+ />
111
+ ))}
112
+ </BestiaryGrid>
113
+ );
114
+
115
+ return (
116
+ <>
117
+ <InformationCenterTabView
118
+ items={bestiaryItems}
119
+ searchQuery={bestiarySearchQuery}
120
+ onSearchChange={setBestiarySearchQuery}
121
+ filterItems={filterItems}
122
+ renderContent={renderContent}
123
+ searchPlaceholder="Search monsters..."
124
+ emptyMessage="No monsters found"
125
+ />
126
+ {tooltipData && (
127
+ <Portal>
128
+ <TooltipWrapper
129
+ style={{
130
+ position: 'fixed',
131
+ left: tooltipData.position.x + 10,
132
+ top: tooltipData.position.y + 10,
133
+ }}
134
+ >
135
+ <InformationCenterNPCTooltip
136
+ npc={tooltipData.npc}
137
+ itemsAtlasJSON={itemsAtlasJSON}
138
+ itemsAtlasIMG={itemsAtlasIMG}
139
+ />
140
+ </TooltipWrapper>
141
+ </Portal>
142
+ )}
143
+ {selectedMonster && (
144
+ <InformationCenterNPCDetails
145
+ npc={selectedMonster}
146
+ itemsAtlasJSON={itemsAtlasJSON}
147
+ itemsAtlasIMG={itemsAtlasIMG}
148
+ entitiesAtlasJSON={entitiesAtlasJSON}
149
+ entitiesAtlasIMG={entitiesAtlasIMG}
150
+ onBack={() => setSelectedMonster(null)}
151
+ />
152
+ )}
153
+ </>
154
+ );
155
+ };
156
+
157
+ const BestiaryGrid = styled.div`
158
+ display: grid;
159
+ grid-template-columns: repeat(4, 1fr);
160
+ gap: 1rem;
161
+ padding: 0 1rem;
162
+ min-height: 300px;
163
+ `;
164
+
165
+ const TooltipWrapper = styled.div`
166
+ position: fixed;
167
+ z-index: 1000;
168
+ pointer-events: none;
169
+ width: 300px;
170
+ `;