colorise world-tree dependent of completion of levels

pull/118/head
Jon Eugster 3 years ago
parent c9a39faa83
commit 58ed29fc17

@ -26,7 +26,6 @@ function GithubIcon({url='https://github.com'}) {
return <div className="github-link"> return <div className="github-link">
<a title="view the Lean game server on Github" href={url}> <a title="view the Lean game server on Github" href={url}>
<svg height="24" aria-hidden="true" viewBox="0 0 16 16" version="1.1" width="24" className=""> <svg height="24" aria-hidden="true" viewBox="0 0 16 16" version="1.1" width="24" className="">
{/* <circle className="world-circle" cx="8" cy="8" r="8" fill="#fff"/> */}
<path fill="#fff" d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z"></path> <path fill="#fff" d="M8 0c4.42 0 8 3.58 8 8a8.013 8.013 0 0 1-5.45 7.59c-.4.08-.55-.17-.55-.38 0-.27.01-1.13.01-2.2 0-.75-.25-1.23-.54-1.48 1.78-.2 3.65-.88 3.65-3.95 0-.88-.31-1.59-.82-2.15.08-.2.36-1.02-.08-2.12 0 0-.67-.22-2.2.82-.64-.18-1.32-.27-2-.27-.68 0-1.36.09-2 .27-1.53-1.03-2.2-.82-2.2-.82-.44 1.1-.16 1.92-.08 2.12-.51.56-.82 1.28-.82 2.15 0 3.06 1.86 3.75 3.64 3.95-.23.2-.44.55-.51 1.07-.46.21-1.61.55-2.33-.66-.15-.24-.6-.83-1.23-.82-.67.01-.27.38.01.53.34.19.73.9.82 1.13.16.45.68 1.31 2.69.94 0 .67.01 1.3.01 1.49 0 .21-.15.45-.55.38A7.995 7.995 0 0 1 0 8c0-4.42 3.58-8 8-8Z"></path>
</svg> </svg>
</a> </a>

@ -18,26 +18,26 @@ import WorldSelectionMenu from './world_selection_menu';
import {PrivacyPolicy} from './privacy_policy'; import {PrivacyPolicy} from './privacy_policy';
import { Button } from './button'; import { Button } from './button';
import { Documentation, Inventory } from './inventory'; import { Documentation, Inventory } from './inventory';
import { store } from '../state/store';
cytoscape.use( klay ); cytoscape.use( klay );
const N = 24 // max number of levels per world const N = 18 // max number of levels per world
const R = 800 // radius of a world const R = 800 // radius of a world
const r = 110 // radius of a level const r = 160 // radius of a level
const s = 100 // global scale const s = 120 // global scale
const padding = 2000 // padding of the graphic (on a different scale) const padding = 2000 // padding of the graphic (on a different scale)
function LevelIcon({ worldId, levelId, position }) { function LevelIcon({ worldId, levelId, position, completed, available }) {
const gameId = React.useContext(GameIdContext) const gameId = React.useContext(GameIdContext)
const completed = useSelector(selectCompleted(gameId, worldId,levelId))
const x = s * position.x + Math.sin(levelId * 2 * Math.PI / N) * (R + 1.2*r + 2*Math.floor((levelId - 1)/N)) const x = s * position.x + Math.sin(levelId * 2 * Math.PI / N) * (R + 1.2*r + 2.4*r*Math.floor((levelId - 1)/N))
const y = s * position.y - Math.cos(levelId * 2 * Math.PI / N) * (R + 1.2*r + 2*Math.floor((levelId - 1)/N)) const y = s * position.y - Math.cos(levelId * 2 * Math.PI / N) * (R + 1.2*r + 2.4*r*Math.floor((levelId - 1)/N))
// TODO: relative positioning? // TODO: relative positioning?
return ( return (
<Link to={`/${gameId}/world/${worldId}/level/${levelId}`}> <Link to={`/${gameId}/world/${worldId}/level/${levelId}`}>
<circle fill={completed ? "green" :"#999"} cx={x} cy={y} r={r} /> <circle fill={completed ? "#139e13" : available? "#1976d2" : "#999"} cx={x} cy={y} r={r} />
</Link> </Link>
) )
} }
@ -72,35 +72,64 @@ function Welcome() {
const svgElements = [] const svgElements = []
// For each `worldId` as index, this contains a list of booleans with indices
// 0, 1, …, n. Index `0` will be set to `false` if any dependency is not completely solved.
// Indices `1, …, n` indicate if the corresponding level is completed
var completed = {}
if (gameInfo.data) { if (gameInfo.data) {
// Fill `completed` with the level data.
for (let worldId in nodes) {
let position: cytoscape.Position = nodes[worldId].position
let state = store.getState()
completed[worldId] = Array.from({ length: gameInfo.data.worldSize[worldId] + 1 }, (_, i) => {
// Index `0` might be set to `false` in the loop over the edges
if (!i) {return true}
return selectCompleted(gameId, worldId, i)(state)
})
}
for (let i in gameInfo.data.worlds.edges) { for (let i in gameInfo.data.worlds.edges) {
const edge = gameInfo.data.worlds.edges[i] const edge = gameInfo.data.worlds.edges[i]
// If the origin world is not completed, mark the target world as non-playable
let unlocked = completed[edge[0]].slice(1).every(Boolean)
if (!unlocked) {completed[edge[1]][0] = false}
// Draw the connection edges
svgElements.push( svgElements.push(
<line key={`pathway${i}`} x1={s*nodes[edge[0]].position.x} y1={s*nodes[edge[0]].position.y} <line key={`pathway${i}`} x1={s*nodes[edge[0]].position.x} y1={s*nodes[edge[0]].position.y}
x2={s*nodes[edge[1]].position.x} y2={s*nodes[edge[1]].position.y} stroke="#1976d2" strokeWidth={s}/> x2={s*nodes[edge[1]].position.x} y2={s*nodes[edge[1]].position.y}
stroke={unlocked ? "green" : "#bbb"} strokeWidth={s}/>
) )
} }
for (let id in nodes) { for (let worldId in nodes) {
let position: cytoscape.Position = nodes[id].position // Draw the level bubbles
let position: cytoscape.Position = nodes[worldId].position
for (let i = 1; i <= gameInfo.data.worldSize[id]; i++) { for (let i = 1; i <= gameInfo.data.worldSize[worldId]; i++) {
svgElements.push( svgElements.push(
<LevelIcon <LevelIcon
key={`/${gameId}/world/${id}/level/${i}`} key={`/${gameId}/world/${worldId}/level/${i}`}
position={position} worldId={id} levelId={i} /> position={position} worldId={worldId} levelId={i}
completed={completed[worldId][i]} available={completed[worldId][i-1]}/>
) )
} }
// Draw the worlds
let worldUnlocked = completed[worldId][0]
let worldCompleted = completed[worldId].slice(1).every(Boolean)
svgElements.push( svgElements.push(
<Link key={`world${id}`} to={`/${gameId}/world/${id}/level/0`}> <Link key={`world${worldId}`} to={`/${gameId}/world/${worldId}/level/0`}>
<circle className="world-circle" cx={s*position.x} cy={s*position.y} r={R} <circle className="world-circle" cx={s*position.x} cy={s*position.y} r={R}
fill="#1976d2"/> fill={worldCompleted ? "green" : worldUnlocked ? "#1976d2": "#999"}/>
<foreignObject className="world-title-wrapper" x={s*position.x} y={s*position.y} <foreignObject className="world-title-wrapper" x={s*position.x} y={s*position.y}
width={1.42*R} height={1.42*R} transform={"translate("+ -.71*R +","+ -.71*R +")"}> width={1.42*R} height={1.42*R} transform={"translate("+ -.71*R +","+ -.71*R +")"}>
<div> <div>
<p className="world-title" style={{fontSize: Math.floor(R/4) + "px"}}> <p className="world-title" style={{fontSize: Math.floor(R/4) + "px"}}>
{nodes[id].data.title ? nodes[id].data.title : id} {nodes[worldId].data.title ? nodes[worldId].data.title : worldId}
</p> </p>
</div> </div>
</foreignObject> </foreignObject>

Loading…
Cancel
Save