Added is vehicle checkbox and vehicle type tokens

This commit is contained in:
Mitchell McCaffrey 2020-05-22 20:43:07 +10:00
parent 00c24c34a4
commit a8bd5ab672
7 changed files with 149 additions and 31 deletions

View File

@ -32,7 +32,7 @@ function Map({
disabledTokens,
loading,
}) {
const { tokens } = useContext(TokenDataContext);
const { tokensById } = useContext(TokenDataContext);
const gridX = map && map.gridX;
const gridY = map && map.gridY;
@ -200,7 +200,7 @@ function Map({
const [isTokenMenuOpen, setIsTokenMenuOpen] = useState(false);
const [tokenMenuOptions, setTokenMenuOptions] = useState({});
const [draggingTokenState, setDraggingTokenState] = useState();
const [draggingTokenOptions, setDraggingTokenOptions] = useState();
function handleTokenMenuOpen(tokenStateId, tokenImage) {
setTokenMenuOptions({ tokenStateId, tokenImage });
setIsTokenMenuOpen(true);
@ -208,22 +208,30 @@ function Map({
const mapTokens =
mapState &&
Object.values(mapState.tokens).map((tokenState) => (
<MapToken
key={tokenState.id}
token={tokens.find((token) => token.id === tokenState.tokenId)}
tokenState={tokenState}
tokenSizePercent={tokenSizePercent}
onTokenStateChange={onMapTokenStateChange}
onTokenMenuOpen={handleTokenMenuOpen}
onTokenDragStart={() => setDraggingTokenState(tokenState)}
onTokenDragEnd={() => setDraggingTokenState(null)}
draggable={
(selectedToolId === "pan" || selectedToolId === "erase") &&
!(tokenState.id in disabledTokens)
}
/>
));
Object.values(mapState.tokens)
.sort(
(a, b) =>
tokensById[b.tokenId].isVehicle - tokensById[a.tokenId].isVehicle
) // Sort so vehicles render below other tokens
.map((tokenState) => (
<MapToken
key={tokenState.id}
token={tokensById[tokenState.tokenId]}
tokenState={tokenState}
tokenSizePercent={tokenSizePercent}
onTokenStateChange={onMapTokenStateChange}
onTokenMenuOpen={handleTokenMenuOpen}
onTokenDragStart={(e) =>
setDraggingTokenOptions({ tokenState, tokenImage: e.target })
}
onTokenDragEnd={() => setDraggingTokenOptions(null)}
draggable={
(selectedToolId === "pan" || selectedToolId === "erase") &&
!(tokenState.id in disabledTokens)
}
mapState={mapState}
/>
));
const tokenMenu = (
<TokenMenu
@ -235,12 +243,17 @@ function Map({
/>
);
const tokenDragOverlay = draggingTokenState && (
const tokenDragOverlay = draggingTokenOptions && (
<TokenDragOverlay
onTokenStateRemove={() => {
onMapTokenStateRemove(draggingTokenState);
setDraggingTokenState(null);
onTokenStateRemove={(state) => {
onMapTokenStateRemove(state);
setDraggingTokenOptions(null);
}}
onTokenStateChange={onMapTokenStateChange}
tokenState={draggingTokenOptions && draggingTokenOptions.tokenState}
tokenImage={draggingTokenOptions && draggingTokenOptions.tokenImage}
token={tokensById[draggingTokenOptions.tokenState.tokenId]}
mapState={mapState}
/>
);

View File

@ -22,6 +22,7 @@ function MapToken({
onTokenDragStart,
onTokenDragEnd,
draggable,
mapState,
}) {
const { userId } = useContext(AuthContext);
const {
@ -41,15 +42,69 @@ function MapToken({
}
}, [tokenSourceImage]);
function handleDragStart(event) {
const tokenImage = event.target;
const tokenImageRect = tokenImage.getClientRect();
if (token.isVehicle) {
// Find all other tokens on the map
const layer = tokenImage.getLayer();
const tokens = layer.find(".token");
for (let other of tokens) {
if (other === tokenImage) {
continue;
}
const otherRect = other.getClientRect();
const otherCenter = {
x: otherRect.x + otherRect.width / 2,
y: otherRect.y + otherRect.height / 2,
};
// Check the other tokens center overlaps this tokens bounding box
if (
otherCenter.x > tokenImageRect.x &&
otherCenter.x < tokenImageRect.x + tokenImageRect.width &&
otherCenter.y > tokenImageRect.y &&
otherCenter.y < tokenImageRect.y + tokenImageRect.height
) {
// Save and restore token position after moving layer
const position = other.absolutePosition();
other.moveTo(tokenImage);
other.absolutePosition(position);
}
}
}
onTokenDragStart(event);
}
function handleDragEnd(event) {
const tokenImage = event.target;
if (token.isVehicle) {
const layer = tokenImage.getLayer();
const mountedTokens = tokenImage.find(".token");
for (let mountedToken of mountedTokens) {
// Save and restore token position after moving layer
const position = mountedToken.absolutePosition();
mountedToken.moveTo(layer);
mountedToken.absolutePosition(position);
onTokenStateChange({
...mapState.tokens[mountedToken.id()],
x: mountedToken.x() / mapWidth,
y: mountedToken.y() / mapHeight,
lastEditedBy: userId,
});
}
}
setPreventMapInteraction(false);
onTokenStateChange({
...tokenState,
x: event.target.x() / mapWidth,
y: event.target.y() / mapHeight,
x: tokenImage.x() / mapWidth,
y: tokenImage.y() / mapHeight,
lastEditedBy: userId,
});
onTokenDragEnd();
onTokenDragEnd(event);
}
function handleClick(event) {
@ -121,8 +176,10 @@ function MapToken({
onTouchEnd={handlePointerUp}
onClick={handleClick}
onDragEnd={handleDragEnd}
onDragStart={onTokenDragStart}
onDragStart={handleDragStart}
opacity={tokenOpacity}
name={token.isVehicle ? "vehicle" : "token"}
id={tokenState.id}
>
<KonvaImage
ref={imageRef}

View File

@ -3,13 +3,41 @@ import { Box, IconButton } from "theme-ui";
import RemoveTokenIcon from "../../icons/RemoveTokenIcon";
import AuthContext from "../../contexts/AuthContext";
import MapInteractionContext from "../../contexts/MapInteractionContext";
function TokenDragOverlay({ onTokenStateRemove }) {
const { setPreventMapInteraction } = useContext(MapInteractionContext);
function TokenDragOverlay({
onTokenStateRemove,
onTokenStateChange,
token,
tokenState,
tokenImage,
mapState,
}) {
const { userId } = useContext(AuthContext);
const { setPreventMapInteraction, mapWidth, mapHeight } = useContext(
MapInteractionContext
);
function handleTokenRemove() {
onTokenStateRemove();
// Handle other tokens when a vehicle gets deleted
if (token.isVehicle) {
const layer = tokenImage.getLayer();
const mountedTokens = tokenImage.find(".token");
for (let mountedToken of mountedTokens) {
// Save and restore token position after moving layer
const position = mountedToken.absolutePosition();
mountedToken.moveTo(layer);
mountedToken.absolutePosition(position);
onTokenStateChange({
...mapState.tokens[mountedToken.id()],
x: mountedToken.x() / mapWidth,
y: mountedToken.y() / mapHeight,
lastEditedBy: userId,
});
}
}
onTokenStateRemove(tokenState);
setPreventMapInteraction(false);
}

View File

@ -1,5 +1,5 @@
import React from "react";
import { Flex, Box, Input, IconButton, Label } from "theme-ui";
import { Flex, Box, Input, IconButton, Label, Checkbox } from "theme-ui";
import ExpandMoreIcon from "../../icons/ExpandMoreIcon";
@ -29,7 +29,7 @@ function TokenSettings({
</Flex>
{showMore && (
<>
<Box my={2} sx={{ flexGrow: 1 }}>
<Box mt={2} sx={{ flexGrow: 1 }}>
<Label htmlFor="name">Name</Label>
<Input
name="name"
@ -39,6 +39,18 @@ function TokenSettings({
my={1}
/>
</Box>
<Box my={2}>
<Label>
<Checkbox
checked={token && token.isVehicle}
disabled={!token || token.type === "default"}
onChange={(e) =>
onSettingsChange("isVehicle", e.target.checked)
}
/>
Vehicle / Mount
</Label>
</Box>
</>
)}
<IconButton

View File

@ -86,6 +86,11 @@ export function TokenDataProvider({ children }) {
const ownedTokens = tokens.filter((token) => token.owner === userId);
const tokensById = tokens.reduce((obj, token) => {
obj[token.id] = token;
return obj;
}, {});
const value = {
tokens,
ownedTokens,
@ -94,6 +99,7 @@ export function TokenDataProvider({ children }) {
updateToken,
putToken,
getToken,
tokensById,
};
return (

View File

@ -64,6 +64,7 @@ function SelectTokensModal({ isOpen, onRequestClose }) {
lastModified: Date.now(),
owner: userId,
defaultSize: 1,
isVehicle: false,
});
setImageLoading(false);
};

View File

@ -85,6 +85,7 @@ export const tokens = Object.keys(tokenSources).map((key) => ({
name: Case.capital(key),
type: "default",
defaultSize: getDefaultTokenSize(key),
isVehicle: false,
}));
export const unknownSource = unknown;