Restructured dice so it is part of the party

Added initial networking plumbing
This commit is contained in:
Mitchell McCaffrey 2020-08-05 16:34:26 +10:00
parent 795f187782
commit d20be94059
11 changed files with 192 additions and 86 deletions

View File

@ -16,7 +16,7 @@ function StyledModal({
isOpen={isOpen}
onRequestClose={onRequestClose}
style={{
overlay: { backgroundColor: "rgba(0, 0, 0, 0.73)" },
overlay: { backgroundColor: "rgba(0, 0, 0, 0.73)", zIndex: 100 },
content: {
backgroundColor: theme.colors.background,
top: "50%",

View File

@ -9,6 +9,8 @@ import D6Icon from "../../icons/D6Icon";
import D4Icon from "../../icons/D4Icon";
import D100Icon from "../../icons/D100Icon";
import ExpandMoreDiceTrayIcon from "../../icons/ExpandMoreDiceTrayIcon";
import ShareDiceOnIcon from "../../icons/ShareDiceOnIcon";
import ShareDiceOffIcon from "../../icons/ShareDiceOffIcon";
import DiceButton from "./DiceButton";
import SelectDiceButton from "./SelectDiceButton";
@ -23,6 +25,8 @@ function DiceButtons({
onDiceLoad,
diceTraySize,
onDiceTraySizeChange,
shareDice,
onShareDiceChange,
}) {
const [currentDice, setCurrentDice] = useState(dice[0]);
@ -129,6 +133,14 @@ function DiceButtons({
>
<ExpandMoreDiceTrayIcon />
</IconButton>
<Divider />
<IconButton
aria-label={shareDice ? "Hide Dice Rolls" : "Share Dice Rolls"}
title={shareDice ? "Hide Dice Rolls" : "Share Dice Rolls"}
onClick={() => onShareDiceChange(!shareDice)}
>
{shareDice ? <ShareDiceOnIcon /> : <ShareDiceOffIcon />}
</IconButton>
</Flex>
);
}

View File

@ -1,85 +1,20 @@
import React, { useEffect, useState } from "react";
import * as BABYLON from "babylonjs";
import React from "react";
import DiceButtons from "./DiceButtons";
import DiceResults from "./DiceResults";
function DiceControls({
diceRefs,
sceneVisibleRef,
onDiceAdd,
onDiceClear,
onDiceReroll,
onDiceLoad,
diceTraySize,
onDiceTraySizeChange,
shareDice,
onShareDiceChage,
diceRolls,
onDiceRollsChange,
}) {
const [diceRolls, setDiceRolls] = useState([]);
// Update dice rolls
useEffect(() => {
// Find the number facing up on a dice object
function getDiceRoll(dice) {
let number = getDiceInstanceRoll(dice.instance);
// If the dice is a d100 add the d10
if (dice.type === "d100") {
const d10Number = getDiceInstanceRoll(dice.d10Instance);
// Both zero set to 100
if (d10Number === 0 && number === 0) {
number = 100;
} else {
number += d10Number;
}
} else if (dice.type === "d10" && number === 0) {
number = 10;
}
return { type: dice.type, roll: number };
}
// Find the number facing up on a mesh instance of a dice
function getDiceInstanceRoll(instance) {
let highestDot = -1;
let highestLocator;
for (let locator of instance.getChildTransformNodes()) {
let dif = locator
.getAbsolutePosition()
.subtract(instance.getAbsolutePosition());
let direction = dif.normalize();
const dot = BABYLON.Vector3.Dot(direction, BABYLON.Vector3.Up());
if (dot > highestDot) {
highestDot = dot;
highestLocator = locator;
}
}
return parseInt(highestLocator.name.slice(12));
}
function updateDiceRolls() {
const die = diceRefs.current;
const sceneVisible = sceneVisibleRef.current;
if (!sceneVisible) {
return;
}
const diceAwake = die.map((dice) => dice.asleep).includes(false);
if (!diceAwake) {
return;
}
let newRolls = [];
for (let i = 0; i < die.length; i++) {
const dice = die[i];
let roll = getDiceRoll(dice);
newRolls[i] = roll;
}
setDiceRolls(newRolls);
}
const updateInterval = setInterval(updateDiceRolls, 100);
return () => {
clearInterval(updateInterval);
};
}, [diceRefs, sceneVisibleRef]);
return (
<>
<div
@ -98,7 +33,7 @@ function DiceControls({
diceRolls={diceRolls}
onDiceClear={() => {
onDiceClear();
setDiceRolls([]);
onDiceRollsChange([]);
}}
onDiceReroll={onDiceReroll}
/>
@ -114,14 +49,13 @@ function DiceControls({
diceRolls={diceRolls}
onDiceAdd={(style, type) => {
onDiceAdd(style, type);
setDiceRolls((prevRolls) => [
...prevRolls,
{ type, roll: "unknown" },
]);
onDiceRollsChange([...diceRolls, { type, roll: "unknown" }]);
}}
onDiceLoad={onDiceLoad}
onDiceTraySizeChange={onDiceTraySizeChange}
diceTraySize={diceTraySize}
shareDice={shareDice}
onShareDiceChange={onShareDiceChage}
/>
</div>
</>

View File

@ -19,7 +19,15 @@ import DiceTray from "../../dice/diceTray/DiceTray";
import DiceLoadingContext from "../../contexts/DiceLoadingContext";
function DiceTrayOverlay({ isOpen }) {
import { getDiceRoll } from "../../helpers/dice";
function DiceTrayOverlay({
isOpen,
shareDice,
onShareDiceChage,
diceRolls,
onDiceRollsChange,
}) {
const sceneRef = useRef();
const shadowGeneratorRef = useRef();
const diceRefs = useRef([]);
@ -251,6 +259,34 @@ function DiceTrayOverlay({ isOpen }) {
};
}, [diceTraySize]);
// Update dice rolls
useEffect(() => {
function updateDiceRolls() {
const die = diceRefs.current;
const sceneVisible = sceneVisibleRef.current;
if (!sceneVisible) {
return;
}
const diceAwake = die.map((dice) => dice.asleep).includes(false);
if (!diceAwake) {
return;
}
let newRolls = [];
for (let i = 0; i < die.length; i++) {
const dice = die[i];
let roll = getDiceRoll(dice);
newRolls[i] = roll;
}
onDiceRollsChange(newRolls);
}
const updateInterval = setInterval(updateDiceRolls, 100);
return () => {
clearInterval(updateInterval);
};
}, [diceRefs, sceneVisibleRef, onDiceRollsChange]);
return (
<Box
sx={{
@ -277,14 +313,16 @@ function DiceTrayOverlay({ isOpen }) {
/>
</Box>
<DiceControls
diceRefs={diceRefs}
sceneVisibleRef={sceneVisibleRef}
onDiceAdd={handleDiceAdd}
onDiceClear={handleDiceClear}
onDiceReroll={handleDiceReroll}
onDiceLoad={handleDiceLoad}
diceTraySize={diceTraySize}
onDiceTraySizeChange={setDiceTraySize}
shareDice={shareDice}
onShareDiceChage={onShareDiceChage}
diceRolls={diceRolls}
onDiceRollsChange={onDiceRollsChange}
/>
{isLoading && (
<Box

View File

@ -6,7 +6,6 @@ import MapInteraction from "./MapInteraction";
import MapToken from "./MapToken";
import MapDrawing from "./MapDrawing";
import MapFog from "./MapFog";
import MapDice from "./MapDice";
import MapGrid from "./MapGrid";
import MapMeasure from "./MapMeasure";
import MapLoadingOverlay from "./MapLoadingOverlay";
@ -338,7 +337,6 @@ function Map({
{mapControls}
{tokenMenu}
{tokenDragOverlay}
<MapDice />
<MapLoadingOverlay />
</>
}

View File

@ -6,7 +6,12 @@ import DiceTrayOverlay from "../dice/DiceTrayOverlay";
import { DiceLoadingProvider } from "../../contexts/DiceLoadingContext";
function MapDice() {
function DiceTrayButton({
shareDice,
onShareDiceChage,
diceRolls,
onDiceRollsChange,
}) {
const [isExpanded, setIsExpanded] = useState(false);
return (
@ -37,10 +42,16 @@ function MapDice() {
<ExpandMoreDiceIcon isExpanded={isExpanded} />
</IconButton>
<DiceLoadingProvider>
<DiceTrayOverlay isOpen={isExpanded} />
<DiceTrayOverlay
isOpen={isExpanded}
shareDice={shareDice}
onShareDiceChage={onShareDiceChage}
diceRolls={diceRolls}
onDiceRollsChange={onDiceRollsChange}
/>
</DiceLoadingProvider>
</Flex>
);
}
export default MapDice;
export default DiceTrayButton;

View File

@ -8,6 +8,7 @@ import StartStreamButton from "./StartStreamButton";
import SettingsButton from "../SettingsButton";
import StartTimerButton from "./StartTimerButton";
import Timer from "./Timer";
import DiceTrayButton from "./DiceTrayButton";
function Party({
nickname,
@ -22,6 +23,10 @@ function Party({
partyTimers,
onTimerStart,
onTimerStop,
shareDice,
onShareDiceChage,
diceRolls,
onDiceRollsChange,
}) {
return (
<Flex
@ -31,8 +36,9 @@ function Party({
flexDirection: "column",
width: "112px",
minWidth: "112px",
overflowY: "auto",
overflow: "visible",
alignItems: "center",
position: "relative",
}}
>
<Box
@ -50,7 +56,12 @@ function Party({
width: "100%",
}}
>
<Nickname nickname={`${nickname} (you)` || ""} />
<Nickname
nickname={`${nickname} (you) ${
shareDice &&
diceRolls.reduce((accumulator, dice) => accumulator + dice.roll, 0)
}`}
/>
{Object.entries(partyNicknames).map(([id, partyNickname]) => (
<Nickname
nickname={partyNickname}
@ -83,6 +94,14 @@ function Party({
/>
<SettingsButton />
</Flex>
<Box sx={{ position: "absolute", top: 0, right: "0", zIndex: 1 }}>
<DiceTrayButton
shareDice={shareDice}
onShareDiceChage={onShareDiceChage}
diceRolls={diceRolls}
onDiceRollsChange={onDiceRollsChange}
/>
</Box>
</Flex>
);
}

43
src/helpers/dice.js Normal file
View File

@ -0,0 +1,43 @@
import * as BABYLON from "babylonjs";
/**
* Find the number facing up on a mesh instance of a dice
* @param {Object} instance The dice instance
*/
export function getDiceInstanceRoll(instance) {
let highestDot = -1;
let highestLocator;
for (let locator of instance.getChildTransformNodes()) {
let dif = locator
.getAbsolutePosition()
.subtract(instance.getAbsolutePosition());
let direction = dif.normalize();
const dot = BABYLON.Vector3.Dot(direction, BABYLON.Vector3.Up());
if (dot > highestDot) {
highestDot = dot;
highestLocator = locator;
}
}
return parseInt(highestLocator.name.slice(12));
}
/**
* Find the number facing up on a dice object
* @param {Object} dice The Dice object
*/
export function getDiceRoll(dice) {
let number = getDiceInstanceRoll(dice.instance);
// If the dice is a d100 add the d10
if (dice.type === "d100") {
const d10Number = getDiceInstanceRoll(dice.d10Instance);
// Both zero set to 100
if (d10Number === 0 && number === 0) {
number = 100;
} else {
number += d10Number;
}
} else if (dice.type === "d10" && number === 0) {
number = 10;
}
return { type: dice.type, roll: number };
}

View File

@ -0,0 +1,18 @@
import React from "react";
function ShareDiceOffIcon() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 0 24 24"
width="24"
fill="currentcolor"
>
<path d="M24 24H0V0h24z" fill="none" />
<path d="M11,8.17L6.49,3.66C8.07,2.61,9.96,2,12,2c5.52,0,10,4.48,10,10c0,2.04-0.61,3.93-1.66,5.51l-1.46-1.46 C19.59,14.87,20,13.48,20,12c0-3.35-2.07-6.22-5-7.41V5c0,1.1-0.9,2-2,2h-2V8.17z M20.49,21.9L20.49,21.9 c-0.39,0.39-1.02,0.39-1.41,0l-1.56-1.56c-2.07,1.37-4.68,2-7.45,1.48c-3.95-0.75-7.13-3.92-7.88-7.88 c-0.52-2.77,0.1-5.38,1.48-7.45L2.1,4.93c-0.39-0.39-0.39-1.02,0-1.41l0,0c0.39-0.39,1.02-0.39,1.41,0l16.97,16.97 C20.88,20.88,20.88,21.51,20.49,21.9z M11,18c-1.1,0-2-0.9-2-2v-1l-4.79-4.79C4.08,10.79,4,11.38,4,12c0,4.08,3.05,7.44,7,7.93V18z" />
</svg>
);
}
export default ShareDiceOffIcon;

View File

@ -0,0 +1,18 @@
import React from "react";
function ShareDiceOnIcon() {
return (
<svg
xmlns="http://www.w3.org/2000/svg"
height="24"
viewBox="0 0 24 24"
width="24"
fill="currentcolor"
>
<path d="M24 24H0V0h24z" fill="none" />
<path d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10 10-4.48 10-10S17.52 2 12 2zm-1 17.93c-3.95-.49-7-3.85-7-7.93 0-.62.08-1.21.21-1.79L9 15v1c0 1.1.9 2 2 2v1.93zm6.9-2.54c-.26-.81-1-1.39-1.9-1.39h-1v-3c0-.55-.45-1-1-1H8v-2h2c.55 0 1-.45 1-1V7h2c1.1 0 2-.9 2-2v-.41c2.93 1.19 5 4.06 5 7.41 0 2.08-.8 3.97-2.1 5.39z" />
</svg>
);
}
export default ShareDiceOnIcon;

View File

@ -25,6 +25,9 @@ function NetworkedParty({ gameId, session }) {
const [partyStreams, setPartyStreams] = useState({});
const [timer, setTimer] = useState(null);
const [partyTimers, setPartyTimers] = useState({});
const [diceRolls, setDiceRolls] = useState([]);
const [shareDice, setShareDice] = useState(false);
const [partyDiceRolls, setPartyDiceRolls] = useState({});
function handleNicknameChange(newNickname) {
setNickname(newNickname);
@ -93,6 +96,14 @@ function NetworkedParty({ gameId, session }) {
};
}, [timer, session]);
function handleDiceRollsChange(newDiceRolls) {
setDiceRolls(newDiceRolls);
}
function handleShareDiceChange(newShareDice) {
setShareDice(newShareDice);
}
useEffect(() => {
function handlePeerConnect({ peer, reply }) {
reply("nickname", { [session.id]: nickname });
@ -191,6 +202,10 @@ function NetworkedParty({ gameId, session }) {
partyTimers={partyTimers}
onTimerStart={handleTimerStart}
onTimerStop={handleTimerStop}
shareDice={shareDice}
onShareDiceChage={handleShareDiceChange}
diceRolls={diceRolls}
onDiceRollsChange={handleDiceRollsChange}
/>
);
}