@rpg-engine/long-bow 0.3.96 → 0.3.97
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/dist/components/CircularController/CircularController.d.ts +1 -0
- package/dist/components/Shortcuts/Shortcuts.d.ts +1 -0
- package/dist/components/Shortcuts/useShortcutCooldown.d.ts +4 -0
- package/dist/components/Spellbook/Spell.d.ts +1 -0
- package/dist/components/Spellbook/Spellbook.d.ts +1 -0
- package/dist/long-bow.cjs.development.js +89 -28
- package/dist/long-bow.cjs.development.js.map +1 -1
- package/dist/long-bow.cjs.production.min.js +1 -1
- package/dist/long-bow.cjs.production.min.js.map +1 -1
- package/dist/long-bow.esm.js +89 -28
- package/dist/long-bow.esm.js.map +1 -1
- package/package.json +1 -1
- package/src/components/CircularController/CircularController.tsx +36 -3
- package/src/components/Item/Cards/ItemInfo.tsx +11 -1
- package/src/components/Shortcuts/Shortcuts.tsx +47 -8
- package/src/components/Shortcuts/SingleShortcut.ts +20 -0
- package/src/components/Shortcuts/useShortcutCooldown.ts +24 -0
- package/src/components/Spellbook/Spell.tsx +27 -2
- package/src/components/Spellbook/Spellbook.tsx +5 -0
package/package.json
CHANGED
|
@@ -11,6 +11,7 @@ import styled from 'styled-components';
|
|
|
11
11
|
import { uiColors } from '../../constants/uiColors';
|
|
12
12
|
import { SpriteFromAtlas } from '../shared/SpriteFromAtlas';
|
|
13
13
|
import { SingleShortcut } from '../Shortcuts/SingleShortcut';
|
|
14
|
+
import { useShortcutCooldown } from '../Shortcuts/useShortcutCooldown';
|
|
14
15
|
|
|
15
16
|
export type CircularControllerProps = {
|
|
16
17
|
onActionClick: () => void;
|
|
@@ -21,6 +22,7 @@ export type CircularControllerProps = {
|
|
|
21
22
|
inventory?: IItemContainer | null;
|
|
22
23
|
atlasIMG: any;
|
|
23
24
|
atlasJSON: any;
|
|
25
|
+
spellCooldowns?: Record<string, number>;
|
|
24
26
|
};
|
|
25
27
|
|
|
26
28
|
export const CircularController: React.FC<CircularControllerProps> = ({
|
|
@@ -32,7 +34,12 @@ export const CircularController: React.FC<CircularControllerProps> = ({
|
|
|
32
34
|
inventory,
|
|
33
35
|
atlasIMG,
|
|
34
36
|
atlasJSON,
|
|
37
|
+
spellCooldowns,
|
|
35
38
|
}) => {
|
|
39
|
+
const { handleShortcutCast, shortcutCooldown } = useShortcutCooldown(
|
|
40
|
+
onShortcutClick
|
|
41
|
+
);
|
|
42
|
+
|
|
36
43
|
const onTouchStart = (e: React.TouchEvent<HTMLButtonElement>) => {
|
|
37
44
|
const target = e.target as HTMLButtonElement;
|
|
38
45
|
target?.classList.add('active');
|
|
@@ -50,6 +57,10 @@ export const CircularController: React.FC<CircularControllerProps> = ({
|
|
|
50
57
|
};
|
|
51
58
|
|
|
52
59
|
const renderShortcut = (i: number) => {
|
|
60
|
+
const buildClassName = (classBase: string, isOnCooldown: boolean) => {
|
|
61
|
+
return `${classBase} ${isOnCooldown ? 'onCooldown' : ''}`;
|
|
62
|
+
};
|
|
63
|
+
|
|
53
64
|
let variant = '';
|
|
54
65
|
|
|
55
66
|
if (i === 0) variant = 'top';
|
|
@@ -57,9 +68,11 @@ export const CircularController: React.FC<CircularControllerProps> = ({
|
|
|
57
68
|
|
|
58
69
|
const onShortcutClickBinded =
|
|
59
70
|
shortcuts[i]?.type !== ShortcutType.None
|
|
60
|
-
?
|
|
71
|
+
? handleShortcutCast.bind(null, i)
|
|
61
72
|
: () => {};
|
|
62
73
|
|
|
74
|
+
const isOnShortcutCooldown = shortcutCooldown > 0;
|
|
75
|
+
|
|
63
76
|
if (shortcuts[i]?.type === ShortcutType.Item) {
|
|
64
77
|
const payload = shortcuts[i]?.payload as IItem | undefined;
|
|
65
78
|
|
|
@@ -88,6 +101,9 @@ export const CircularController: React.FC<CircularControllerProps> = ({
|
|
|
88
101
|
disabled={false}
|
|
89
102
|
className={variant}
|
|
90
103
|
>
|
|
104
|
+
{isOnShortcutCooldown && (
|
|
105
|
+
<span className="cooldown">{shortcutCooldown.toFixed(1)}</span>
|
|
106
|
+
)}
|
|
91
107
|
{payload && (
|
|
92
108
|
<SpriteFromAtlas
|
|
93
109
|
atlasIMG={atlasIMG}
|
|
@@ -108,13 +124,23 @@ export const CircularController: React.FC<CircularControllerProps> = ({
|
|
|
108
124
|
containerStyle={{ pointerEvents: 'none' }}
|
|
109
125
|
/>
|
|
110
126
|
)}
|
|
111
|
-
<span className=
|
|
127
|
+
<span className={buildClassName('qty', isOnShortcutCooldown)}>
|
|
128
|
+
{totalQty}
|
|
129
|
+
</span>
|
|
112
130
|
</StyledShortcut>
|
|
113
131
|
);
|
|
114
132
|
}
|
|
115
133
|
|
|
116
134
|
const payload = shortcuts[i]?.payload as IRawSpell | undefined;
|
|
117
135
|
|
|
136
|
+
const spellCooldown = !payload
|
|
137
|
+
? 0
|
|
138
|
+
: spellCooldowns?.[payload.magicWords.replaceAll(' ', '_')] ??
|
|
139
|
+
shortcutCooldown;
|
|
140
|
+
const cooldown =
|
|
141
|
+
spellCooldown > shortcutCooldown ? spellCooldown : shortcutCooldown;
|
|
142
|
+
const isOnCooldown = cooldown > 0 && !!payload;
|
|
143
|
+
|
|
118
144
|
return (
|
|
119
145
|
<StyledShortcut
|
|
120
146
|
key={i}
|
|
@@ -123,7 +149,14 @@ export const CircularController: React.FC<CircularControllerProps> = ({
|
|
|
123
149
|
disabled={mana < (payload?.manaCost ?? 0)}
|
|
124
150
|
className={variant}
|
|
125
151
|
>
|
|
126
|
-
|
|
152
|
+
{isOnCooldown && (
|
|
153
|
+
<span className="cooldown">
|
|
154
|
+
{cooldown.toFixed(cooldown < 10 ? 1 : 0)}
|
|
155
|
+
</span>
|
|
156
|
+
)}
|
|
157
|
+
<span className={buildClassName('mana', isOnCooldown)}>
|
|
158
|
+
{payload && payload.manaCost}
|
|
159
|
+
</span>
|
|
127
160
|
<span className="magicWords">
|
|
128
161
|
{payload?.magicWords.split(' ').map(word => word[0])}
|
|
129
162
|
</span>
|
|
@@ -142,8 +142,10 @@ export const ItemInfo: React.FC<IItemInfoProps> = ({
|
|
|
142
142
|
|
|
143
143
|
{item.minRequirements && (
|
|
144
144
|
<LevelRequirement>
|
|
145
|
-
<div>
|
|
145
|
+
<div className="title">Requirements:</div>
|
|
146
|
+
<div>- Level: {item.minRequirements.level}</div>
|
|
146
147
|
<div>
|
|
148
|
+
-{' '}
|
|
147
149
|
{item.minRequirements.skill.name[0].toUpperCase() +
|
|
148
150
|
item.minRequirements.skill.name.slice(1)}
|
|
149
151
|
: {item.minRequirements.skill.level}
|
|
@@ -222,6 +224,14 @@ const LevelRequirement = styled.div`
|
|
|
222
224
|
margin-top: 0.2rem;
|
|
223
225
|
margin-bottom: 1rem;
|
|
224
226
|
color: ${uiColors.orange};
|
|
227
|
+
|
|
228
|
+
.title {
|
|
229
|
+
margin-bottom: 4px;
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
div {
|
|
233
|
+
margin-bottom: 2px;
|
|
234
|
+
}
|
|
225
235
|
`;
|
|
226
236
|
|
|
227
237
|
const Statistic = styled.div<{ $isSpecial?: boolean }>`
|
|
@@ -12,6 +12,7 @@ import { uiColors } from '../../constants/uiColors';
|
|
|
12
12
|
import { countItemFromInventory } from '../../libs/itemCounter';
|
|
13
13
|
import { SpriteFromAtlas } from '../shared/SpriteFromAtlas';
|
|
14
14
|
import { SingleShortcut } from './SingleShortcut';
|
|
15
|
+
import { useShortcutCooldown } from './useShortcutCooldown';
|
|
15
16
|
|
|
16
17
|
export type ShortcutsProps = {
|
|
17
18
|
shortcuts: IShortcut[];
|
|
@@ -21,6 +22,7 @@ export type ShortcutsProps = {
|
|
|
21
22
|
inventory?: IItemContainer | null;
|
|
22
23
|
atlasJSON: any;
|
|
23
24
|
atlasIMG: any;
|
|
25
|
+
spellCooldowns?: Record<string, number>;
|
|
24
26
|
};
|
|
25
27
|
|
|
26
28
|
export const Shortcuts: React.FC<ShortcutsProps> = ({
|
|
@@ -31,16 +33,21 @@ export const Shortcuts: React.FC<ShortcutsProps> = ({
|
|
|
31
33
|
atlasJSON,
|
|
32
34
|
atlasIMG,
|
|
33
35
|
inventory,
|
|
36
|
+
spellCooldowns,
|
|
34
37
|
}) => {
|
|
35
38
|
const shortcutsRefs = useRef<HTMLButtonElement[]>([]);
|
|
36
39
|
|
|
40
|
+
const { handleShortcutCast, shortcutCooldown } = useShortcutCooldown(
|
|
41
|
+
onShortcutCast
|
|
42
|
+
);
|
|
43
|
+
|
|
37
44
|
useEffect(() => {
|
|
38
45
|
const handleKeyDown = (e: KeyboardEvent) => {
|
|
39
46
|
if (isBlockedCastingByKeyboard) return;
|
|
40
47
|
|
|
41
48
|
const shortcutIndex = Number(e.key) - 1;
|
|
42
49
|
if (shortcutIndex >= 0 && shortcutIndex <= 5) {
|
|
43
|
-
|
|
50
|
+
handleShortcutCast(shortcutIndex);
|
|
44
51
|
shortcutsRefs.current[shortcutIndex]?.classList.add('active');
|
|
45
52
|
setTimeout(() => {
|
|
46
53
|
shortcutsRefs.current[shortcutIndex]?.classList.remove('active');
|
|
@@ -53,11 +60,17 @@ export const Shortcuts: React.FC<ShortcutsProps> = ({
|
|
|
53
60
|
return () => {
|
|
54
61
|
window.removeEventListener('keydown', handleKeyDown);
|
|
55
62
|
};
|
|
56
|
-
}, [shortcuts, isBlockedCastingByKeyboard]);
|
|
63
|
+
}, [shortcuts, isBlockedCastingByKeyboard, shortcutCooldown]);
|
|
57
64
|
|
|
58
65
|
return (
|
|
59
66
|
<List>
|
|
60
67
|
{Array.from({ length: 6 }).map((_, i) => {
|
|
68
|
+
const buildClassName = (classBase: string, isOnCooldown: boolean) => {
|
|
69
|
+
return `${classBase} ${isOnCooldown ? 'onCooldown' : ''}`;
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
const isOnShortcutCooldown = shortcutCooldown > 0;
|
|
73
|
+
|
|
61
74
|
if (shortcuts[i]?.type === ShortcutType.Item) {
|
|
62
75
|
const payload = shortcuts[i]?.payload as IItem | undefined;
|
|
63
76
|
|
|
@@ -81,12 +94,15 @@ export const Shortcuts: React.FC<ShortcutsProps> = ({
|
|
|
81
94
|
return (
|
|
82
95
|
<StyledShortcut
|
|
83
96
|
key={i}
|
|
84
|
-
onPointerDown={
|
|
97
|
+
onPointerDown={handleShortcutCast.bind(null, i)}
|
|
85
98
|
disabled={false}
|
|
86
99
|
ref={el => {
|
|
87
100
|
if (el) shortcutsRefs.current[i] = el;
|
|
88
101
|
}}
|
|
89
102
|
>
|
|
103
|
+
{isOnShortcutCooldown && (
|
|
104
|
+
<span className="cooldown">{shortcutCooldown.toFixed(1)}</span>
|
|
105
|
+
)}
|
|
90
106
|
{payload && (
|
|
91
107
|
<SpriteFromAtlas
|
|
92
108
|
atlasIMG={atlasIMG}
|
|
@@ -104,28 +120,51 @@ export const Shortcuts: React.FC<ShortcutsProps> = ({
|
|
|
104
120
|
height={32}
|
|
105
121
|
/>
|
|
106
122
|
)}
|
|
107
|
-
<span className=
|
|
108
|
-
|
|
123
|
+
<span className={buildClassName('qty', isOnShortcutCooldown)}>
|
|
124
|
+
{totalQty}
|
|
125
|
+
</span>
|
|
126
|
+
<span
|
|
127
|
+
className={buildClassName('keyboard', isOnShortcutCooldown)}
|
|
128
|
+
>
|
|
129
|
+
{i + 1}
|
|
130
|
+
</span>
|
|
109
131
|
</StyledShortcut>
|
|
110
132
|
);
|
|
111
133
|
}
|
|
112
134
|
|
|
113
135
|
const payload = shortcuts[i]?.payload as IRawSpell | undefined;
|
|
114
136
|
|
|
137
|
+
const spellCooldown = !payload
|
|
138
|
+
? 0
|
|
139
|
+
: spellCooldowns?.[payload.magicWords.replaceAll(' ', '_')] ??
|
|
140
|
+
shortcutCooldown;
|
|
141
|
+
const cooldown =
|
|
142
|
+
spellCooldown > shortcutCooldown ? spellCooldown : shortcutCooldown;
|
|
143
|
+
const isOnCooldown = cooldown > 0 && !!payload;
|
|
144
|
+
|
|
115
145
|
return (
|
|
116
146
|
<StyledShortcut
|
|
117
147
|
key={i}
|
|
118
|
-
onPointerDown={
|
|
148
|
+
onPointerDown={handleShortcutCast.bind(null, i)}
|
|
119
149
|
disabled={mana < (payload?.manaCost ?? 0)}
|
|
120
150
|
ref={el => {
|
|
121
151
|
if (el) shortcutsRefs.current[i] = el;
|
|
122
152
|
}}
|
|
123
153
|
>
|
|
124
|
-
|
|
154
|
+
{isOnCooldown && (
|
|
155
|
+
<span className="cooldown">
|
|
156
|
+
{cooldown.toFixed(cooldown < 10 ? 1 : 0)}
|
|
157
|
+
</span>
|
|
158
|
+
)}
|
|
159
|
+
<span className={buildClassName('mana', isOnCooldown)}>
|
|
160
|
+
{payload && payload.manaCost}
|
|
161
|
+
</span>
|
|
125
162
|
<span className="magicWords">
|
|
126
163
|
{payload?.magicWords.split(' ').map(word => word[0])}
|
|
127
164
|
</span>
|
|
128
|
-
<span className=
|
|
165
|
+
<span className={buildClassName('keyboard', isOnCooldown)}>
|
|
166
|
+
{i + 1}
|
|
167
|
+
</span>
|
|
129
168
|
</StyledShortcut>
|
|
130
169
|
);
|
|
131
170
|
})}
|
|
@@ -46,6 +46,26 @@ export const SingleShortcut = styled.button`
|
|
|
46
46
|
color: ${uiColors.yellow};
|
|
47
47
|
}
|
|
48
48
|
|
|
49
|
+
.onCooldown {
|
|
50
|
+
color: ${uiColors.gray};
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
.cooldown {
|
|
54
|
+
position: absolute;
|
|
55
|
+
z-index: 1;
|
|
56
|
+
top: 0;
|
|
57
|
+
left: 0;
|
|
58
|
+
width: 100%;
|
|
59
|
+
height: 100%;
|
|
60
|
+
border-radius: inherit;
|
|
61
|
+
background-color: rgba(0 0 0 / 60%);
|
|
62
|
+
font-size: 0.7rem;
|
|
63
|
+
display: flex;
|
|
64
|
+
align-items: center;
|
|
65
|
+
justify-content: center;
|
|
66
|
+
color: ${uiColors.darkYellow};
|
|
67
|
+
}
|
|
68
|
+
|
|
49
69
|
&:hover,
|
|
50
70
|
&:focus {
|
|
51
71
|
background-color: ${uiColors.darkGray};
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useEffect, useRef, useState } from "react";
|
|
2
|
+
|
|
3
|
+
export const useShortcutCooldown = (onShortcutCast: (index: number) => void) => {
|
|
4
|
+
const [shortcutCooldown, setShortcutCooldown] = useState(0);
|
|
5
|
+
const cooldownTimeout = useRef<NodeJS.Timeout | null>(null);
|
|
6
|
+
|
|
7
|
+
const handleShortcutCast = (index: number) => {
|
|
8
|
+
console.log(shortcutCooldown);
|
|
9
|
+
if (shortcutCooldown <= 0) setShortcutCooldown(1.5);
|
|
10
|
+
onShortcutCast(index);
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (cooldownTimeout.current) clearTimeout(cooldownTimeout.current);
|
|
15
|
+
|
|
16
|
+
if (shortcutCooldown > 0) {
|
|
17
|
+
cooldownTimeout.current = setTimeout(() => {
|
|
18
|
+
setShortcutCooldown(shortcutCooldown - 0.1);
|
|
19
|
+
}, 100);
|
|
20
|
+
}
|
|
21
|
+
}, [shortcutCooldown]);
|
|
22
|
+
|
|
23
|
+
return { shortcutCooldown, handleShortcutCast };
|
|
24
|
+
};
|
|
@@ -10,6 +10,7 @@ interface Props extends IRawSpell {
|
|
|
10
10
|
onPointerUp?: (spellKey: string) => void;
|
|
11
11
|
isSettingShortcut?: boolean;
|
|
12
12
|
spellKey: string;
|
|
13
|
+
activeCooldown?: number;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
16
|
export const Spell: React.FC<Props> = ({
|
|
@@ -23,6 +24,7 @@ export const Spell: React.FC<Props> = ({
|
|
|
23
24
|
onPointerUp,
|
|
24
25
|
isSettingShortcut,
|
|
25
26
|
minMagicLevelRequired,
|
|
27
|
+
activeCooldown,
|
|
26
28
|
}) => {
|
|
27
29
|
const disabled = isSettingShortcut
|
|
28
30
|
? charMagicLevel < minMagicLevelRequired
|
|
@@ -30,7 +32,7 @@ export const Spell: React.FC<Props> = ({
|
|
|
30
32
|
|
|
31
33
|
return (
|
|
32
34
|
<Container
|
|
33
|
-
disabled={disabled}
|
|
35
|
+
disabled={disabled || (activeCooldown ?? 0) > 0}
|
|
34
36
|
onPointerUp={onPointerUp?.bind(null, spellKey)}
|
|
35
37
|
isSettingShortcut={isSettingShortcut && !disabled}
|
|
36
38
|
className="spell"
|
|
@@ -42,7 +44,14 @@ export const Spell: React.FC<Props> = ({
|
|
|
42
44
|
: manaCost > charMana && 'No mana'}
|
|
43
45
|
</Overlay>
|
|
44
46
|
)}
|
|
45
|
-
<SpellImage>
|
|
47
|
+
<SpellImage>
|
|
48
|
+
{activeCooldown && activeCooldown > 0 ? (
|
|
49
|
+
<span className="cooldown">
|
|
50
|
+
{activeCooldown.toFixed(activeCooldown > 10 ? 0 : 1)}
|
|
51
|
+
</span>
|
|
52
|
+
) : null}
|
|
53
|
+
{magicWords.split(' ').map(word => word[0])}
|
|
54
|
+
</SpellImage>
|
|
46
55
|
<Info>
|
|
47
56
|
<Title>
|
|
48
57
|
<span>{name}</span>
|
|
@@ -110,6 +119,22 @@ const SpellImage = styled.div`
|
|
|
110
119
|
justify-content: center;
|
|
111
120
|
align-items: center;
|
|
112
121
|
text-transform: uppercase;
|
|
122
|
+
position: relative;
|
|
123
|
+
overflow: hidden;
|
|
124
|
+
|
|
125
|
+
.cooldown {
|
|
126
|
+
position: absolute;
|
|
127
|
+
top: 0;
|
|
128
|
+
left: 0;
|
|
129
|
+
width: 100%;
|
|
130
|
+
height: 100%;
|
|
131
|
+
background-color: rgba(0 0 0 / 20%);
|
|
132
|
+
color: ${uiColors.darkYellow};
|
|
133
|
+
font-weight: bold;
|
|
134
|
+
display: flex;
|
|
135
|
+
justify-content: center;
|
|
136
|
+
align-items: center;
|
|
137
|
+
}
|
|
113
138
|
`;
|
|
114
139
|
|
|
115
140
|
const Info = styled.span`
|
|
@@ -22,6 +22,7 @@ export interface ISpellbookProps {
|
|
|
22
22
|
atlasIMG: any;
|
|
23
23
|
atlasJSON: any;
|
|
24
24
|
scale?: number;
|
|
25
|
+
spellCooldowns?: Record<string, number>;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
export const Spellbook: React.FC<ISpellbookProps> = ({
|
|
@@ -38,6 +39,7 @@ export const Spellbook: React.FC<ISpellbookProps> = ({
|
|
|
38
39
|
atlasIMG,
|
|
39
40
|
atlasJSON,
|
|
40
41
|
scale,
|
|
42
|
+
spellCooldowns,
|
|
41
43
|
}) => {
|
|
42
44
|
const [search, setSearch] = useState('');
|
|
43
45
|
const [settingShortcutIndex, setSettingShortcutIndex] = useState(-1);
|
|
@@ -118,6 +120,9 @@ export const Spellbook: React.FC<ISpellbookProps> = ({
|
|
|
118
120
|
}
|
|
119
121
|
spellKey={spell.key}
|
|
120
122
|
isSettingShortcut={settingShortcutIndex !== -1}
|
|
123
|
+
activeCooldown={
|
|
124
|
+
spellCooldowns?.[spell.magicWords.replaceAll(' ', '_')]
|
|
125
|
+
}
|
|
121
126
|
{...spell}
|
|
122
127
|
/>
|
|
123
128
|
</Fragment>
|