2024-07-14 07:24:15 -05:00
|
|
|
import {useEffect, useMemo, useRef, useState} from 'react';
|
2024-07-12 02:10:22 -05:00
|
|
|
|
2024-07-20 04:32:33 -05:00
|
|
|
import {useDomScale} from '@/react/context/dom-scale.js';
|
|
|
|
import {useRadians} from '@/react/context/radians.js';
|
2024-07-20 04:41:00 -05:00
|
|
|
import {RESOLUTION} from '@/util/constants.js';
|
|
|
|
import {render} from '@/util/dialogue.js';
|
2024-07-12 02:10:22 -05:00
|
|
|
|
2024-07-23 12:53:12 -05:00
|
|
|
import DialogueCaret from './dialogue-caret.jsx';
|
2024-07-12 02:10:22 -05:00
|
|
|
import styles from './dialogue.module.css';
|
|
|
|
|
|
|
|
export default function Dialogue({
|
|
|
|
camera,
|
|
|
|
dialogue,
|
|
|
|
scale,
|
|
|
|
}) {
|
|
|
|
const domScale = useDomScale();
|
|
|
|
const ref = useRef();
|
|
|
|
const [dimensions, setDimensions] = useState({h: 0, w: 0});
|
2024-07-13 03:02:55 -05:00
|
|
|
const [caret, setCaret] = useState(0);
|
2024-07-14 21:07:46 -05:00
|
|
|
const radians = useRadians();
|
2024-07-13 17:08:23 -05:00
|
|
|
useEffect(() => {
|
|
|
|
return dialogue.addSkipListener(() => {
|
|
|
|
if (caret >= dialogue.letters.length - 1) {
|
|
|
|
dialogue.onClose();
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
setCaret(dialogue.letters.length - 1);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}, [caret, dialogue]);
|
2024-07-13 03:02:55 -05:00
|
|
|
useEffect(() => {
|
|
|
|
const {params} = dialogue.letters[caret];
|
|
|
|
let handle;
|
|
|
|
if (caret >= dialogue.letters.length - 1) {
|
2024-07-13 17:08:23 -05:00
|
|
|
const {linger} = dialogue;
|
2024-07-13 03:02:55 -05:00
|
|
|
if (!linger) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
handle = setTimeout(() => {
|
|
|
|
dialogue.onClose();
|
|
|
|
}, linger * 1000);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
let jump = caret;
|
|
|
|
while (0 === dialogue.letters[jump].params.rate.frequency && jump < dialogue.letters.length - 1) {
|
|
|
|
jump += 1;
|
|
|
|
}
|
|
|
|
setCaret(jump);
|
|
|
|
if (jump < dialogue.letters.length - 1) {
|
|
|
|
handle = setTimeout(() => {
|
|
|
|
setCaret(caret + 1);
|
|
|
|
}, params.rate.frequency * 1000)
|
|
|
|
}
|
2024-07-12 02:10:22 -05:00
|
|
|
}
|
2024-07-13 03:02:55 -05:00
|
|
|
return () => {
|
|
|
|
clearTimeout(handle);
|
|
|
|
}
|
|
|
|
}, [caret, dialogue]);
|
2024-07-12 02:10:22 -05:00
|
|
|
useEffect(() => {
|
|
|
|
let handle;
|
|
|
|
function track() {
|
|
|
|
if (ref.current) {
|
|
|
|
const {height, width} = ref.current.getBoundingClientRect();
|
|
|
|
setDimensions({h: height / domScale, w: width / domScale});
|
|
|
|
}
|
|
|
|
handle = requestAnimationFrame(track);
|
|
|
|
}
|
|
|
|
handle = requestAnimationFrame(track);
|
|
|
|
return () => {
|
|
|
|
cancelAnimationFrame(handle);
|
|
|
|
};
|
|
|
|
}, [dialogue, domScale, ref]);
|
2024-07-14 07:24:15 -05:00
|
|
|
const localRender = useMemo(
|
|
|
|
() => render(dialogue.letters, styles.letter),
|
|
|
|
[dialogue.letters],
|
|
|
|
);
|
2024-07-22 04:19:29 -05:00
|
|
|
let position = 'function' === typeof dialogue.position
|
|
|
|
? dialogue.position()
|
|
|
|
: dialogue.position || {x: 0, y: 0};
|
|
|
|
position = {
|
|
|
|
x: position.x + dialogue.offset.x,
|
|
|
|
y: position.y + dialogue.offset.y,
|
|
|
|
};
|
2024-07-12 02:10:22 -05:00
|
|
|
const bounds = {
|
|
|
|
x: dimensions.w / (2 * scale),
|
|
|
|
y: dimensions.h / (2 * scale),
|
|
|
|
};
|
|
|
|
const left = Math.max(
|
|
|
|
Math.min(
|
2024-07-30 14:05:27 -05:00
|
|
|
position.x * scale,
|
|
|
|
RESOLUTION.x - bounds.x * scale - 16 + camera.x,
|
2024-07-12 02:10:22 -05:00
|
|
|
),
|
2024-07-30 14:05:27 -05:00
|
|
|
bounds.x * scale + 16 + camera.x,
|
2024-07-12 02:10:22 -05:00
|
|
|
);
|
|
|
|
const top = Math.max(
|
|
|
|
Math.min(
|
2024-07-30 14:05:27 -05:00
|
|
|
position.y * scale - dimensions.h / 2,
|
|
|
|
RESOLUTION.y - bounds.y * scale - 16 + camera.y,
|
2024-07-12 02:10:22 -05:00
|
|
|
),
|
2024-07-30 14:05:27 -05:00
|
|
|
bounds.y * scale + 16 + camera.y,
|
2024-07-12 02:10:22 -05:00
|
|
|
);
|
|
|
|
return (
|
|
|
|
<div
|
2024-07-13 16:56:47 -05:00
|
|
|
className={styles.dialogue}
|
2024-07-12 02:10:22 -05:00
|
|
|
ref={ref}
|
|
|
|
style={{
|
2024-07-30 14:05:27 -05:00
|
|
|
translate: `
|
|
|
|
${left}px
|
|
|
|
${top}px
|
|
|
|
`,
|
2024-07-12 02:10:22 -05:00
|
|
|
}}
|
|
|
|
>
|
2024-07-23 12:53:12 -05:00
|
|
|
<DialogueCaret
|
|
|
|
camera={camera}
|
|
|
|
dialogue={dialogue}
|
|
|
|
dimensions={dimensions}
|
|
|
|
scale={scale}
|
|
|
|
/>
|
2024-07-13 16:56:47 -05:00
|
|
|
<p className={styles.letters}>
|
2024-07-14 07:24:15 -05:00
|
|
|
{localRender(caret, radians)}
|
2024-07-13 16:56:47 -05:00
|
|
|
</p>
|
2024-07-12 02:10:22 -05:00
|
|
|
</div>
|
|
|
|
);
|
|
|
|
}
|