Compare commits
No commits in common. "b2e9e784e555866350f6c2b4dd3d3c00dd6359d3" and "1c9e4d5aa4f8fa7af06e71a6c9b18a2e97e8fd47" have entirely different histories.
b2e9e784e5
...
1c9e4d5aa4
10
package-lock.json
generated
10
package-lock.json
generated
@ -12,7 +12,6 @@
|
|||||||
"@types/lodash": "^4.17.23",
|
"@types/lodash": "^4.17.23",
|
||||||
"@types/three": "^0.182.0",
|
"@types/three": "^0.182.0",
|
||||||
"lodash": "^4.17.23",
|
"lodash": "^4.17.23",
|
||||||
"lucide-react": "^0.575.0",
|
|
||||||
"popmotion": "^11.0.5",
|
"popmotion": "^11.0.5",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
@ -2788,15 +2787,6 @@
|
|||||||
"yallist": "^3.0.2"
|
"yallist": "^3.0.2"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"node_modules/lucide-react": {
|
|
||||||
"version": "0.575.0",
|
|
||||||
"resolved": "https://registry.npmjs.org/lucide-react/-/lucide-react-0.575.0.tgz",
|
|
||||||
"integrity": "sha512-VuXgKZrk0uiDlWjGGXmKV6MSk9Yy4l10qgVvzGn2AWBx1Ylt0iBexKOAoA6I7JO3m+M9oeovJd3yYENfkUbOeg==",
|
|
||||||
"license": "ISC",
|
|
||||||
"peerDependencies": {
|
|
||||||
"react": "^16.5.1 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"node_modules/meshoptimizer": {
|
"node_modules/meshoptimizer": {
|
||||||
"version": "0.22.0",
|
"version": "0.22.0",
|
||||||
"resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.22.0.tgz",
|
"resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.22.0.tgz",
|
||||||
|
|||||||
@ -14,7 +14,6 @@
|
|||||||
"@types/lodash": "^4.17.23",
|
"@types/lodash": "^4.17.23",
|
||||||
"@types/three": "^0.182.0",
|
"@types/three": "^0.182.0",
|
||||||
"lodash": "^4.17.23",
|
"lodash": "^4.17.23",
|
||||||
"lucide-react": "^0.575.0",
|
|
||||||
"popmotion": "^11.0.5",
|
"popmotion": "^11.0.5",
|
||||||
"react": "^19.2.0",
|
"react": "^19.2.0",
|
||||||
"react-dom": "^19.2.0",
|
"react-dom": "^19.2.0",
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 2.1 MiB |
@ -1,12 +1,10 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { Play, Bug, StepForward, RotateCcw, Square, ChevronUp, ChevronDown, LayoutGrid } from "lucide-react";
|
|
||||||
import { CodeBlockContainer, INIT_CODEBLOCKS, CodeBlock, INSERT_CODEBLOCK, REMOVE_CODEBLOCK, SET_DROPZONE_Z_INDEX } from "../store/codeBlocks/types";
|
import { CodeBlockContainer, INIT_CODEBLOCKS, CodeBlock, INSERT_CODEBLOCK, REMOVE_CODEBLOCK, SET_DROPZONE_Z_INDEX } from "../store/codeBlocks/types";
|
||||||
import { LevelInitializer, IPlayerData } from "../game/levels";
|
import { LevelInitializer, IPlayerData } from "../game/levels";
|
||||||
import { initCodeBlocks, insertCodeBlock, removeCodeBlock } from "../store/codeBlocks/actions";
|
import { initCodeBlocks, insertCodeBlock, removeCodeBlock } from "../store/codeBlocks/actions";
|
||||||
import { AppState } from "../store";
|
import { AppState } from "../store";
|
||||||
import game from "../game/game"
|
import game from "../game/game"
|
||||||
import { connect } from "react-redux";
|
import { connect } from "react-redux";
|
||||||
import { Link } from "react-router-dom";
|
|
||||||
import { START_EXECUTING, STEP_EXECUTION, TOGGLE_AUTOSTEP, TOGGLE_SPEED } from "../store/executionState/types";
|
import { START_EXECUTING, STEP_EXECUTION, TOGGLE_AUTOSTEP, TOGGLE_SPEED } from "../store/executionState/types";
|
||||||
|
|
||||||
|
|
||||||
@ -102,7 +100,7 @@ const FnContainers = ({ code, dropZoneZIdx, setDropZoneZIdx, remove, insert }) =
|
|||||||
code.map((cv, idx) => {
|
code.map((cv, idx) => {
|
||||||
return (
|
return (
|
||||||
<div className="fncontainer" key={idx} >
|
<div className="fncontainer" key={idx} >
|
||||||
<h2>{cv.name + "(){"}</h2>
|
<h3>{cv.name + "(){"}</h3>
|
||||||
<div style={{ height: Math.floor(cv.nMaxBlocks / 4 + 1) * 4 + 'rem' }}>
|
<div style={{ height: Math.floor(cv.nMaxBlocks / 4 + 1) * 4 + 'rem' }}>
|
||||||
<OpIndicators n={cv.nMaxBlocks} />
|
<OpIndicators n={cv.nMaxBlocks} />
|
||||||
<div className="ops" >
|
<div className="ops" >
|
||||||
@ -120,7 +118,7 @@ const FnContainers = ({ code, dropZoneZIdx, setDropZoneZIdx, remove, insert }) =
|
|||||||
insert={insert}
|
insert={insert}
|
||||||
style={{ zIndex: dropZoneZIdx }} />
|
style={{ zIndex: dropZoneZIdx }} />
|
||||||
</div>
|
</div>
|
||||||
<h2>{"}"}</h2>
|
<h3>{"}"}</h3>
|
||||||
</div>)
|
</div>)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -145,60 +143,48 @@ const OpsPalette = ({ ops, setDropZoneZIdx }) => {
|
|||||||
|
|
||||||
const RuntimeControls = ({ start, fast, step, running, level, code, autostep, toggleAutoStep, toggleSpeed, finished }) => {
|
const RuntimeControls = ({ start, fast, step, running, level, code, autostep, toggleAutoStep, toggleSpeed, finished }) => {
|
||||||
let DebugButton = () => (
|
let DebugButton = () => (
|
||||||
<div className="controlbutton debug"
|
<div className="controlbutton"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
start(level, code, level.playerPos)
|
start(level, code, level.playerPos)
|
||||||
}}>
|
}}>
|
||||||
<Bug size={16} />
|
debug
|
||||||
<span>debug</span>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
let StepButton = () => (
|
let StepButton = () => (
|
||||||
<div className="controlbutton step"
|
<div className="controlbutton"
|
||||||
onClick={step}>
|
onClick={step}>
|
||||||
<StepForward size={16} />
|
step
|
||||||
<span>step</span>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
let StartButton = () => (
|
let StartButton = () => (
|
||||||
<div className="controlbutton run"
|
<div className="controlbutton"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
start(level, code, level.playerPos)
|
start(level, code, level.playerPos)
|
||||||
toggleAutoStep()
|
toggleAutoStep()
|
||||||
step()
|
step()
|
||||||
}}>
|
}}>
|
||||||
<Play size={16} />
|
run
|
||||||
<span>run</span>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
let LevelsButton = () => (
|
|
||||||
<Link to="/levels/" className="controlbutton levels">
|
|
||||||
<LayoutGrid size={16} />
|
|
||||||
<span>levels</span>
|
|
||||||
</Link>
|
|
||||||
)
|
|
||||||
let ResetButton = () => (
|
let ResetButton = () => (
|
||||||
<div className="controlbutton reset"
|
<div className="controlbutton"
|
||||||
onClick={() => game.reset()}>
|
onClick={() => game.reset()}>
|
||||||
<RotateCcw size={16} />
|
reset
|
||||||
<span>reset</span>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
let ToggleSpeedButton = () => (
|
let ToggleSpeedButton = () => (
|
||||||
<div className="controlbutton speed"
|
<div className="controlbutton"
|
||||||
onClick={toggleSpeed}>
|
onClick={toggleSpeed}>
|
||||||
{fast ? <ChevronDown size={16} /> : <ChevronUp size={16} />}
|
{fast ? "slower" : "faster"}
|
||||||
<span>{fast ? "slower" : "faster"}</span>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
let AbortButton = () => (
|
let AbortButton = () => (
|
||||||
<div className="controlbutton abort"
|
<div className="controlbutton"
|
||||||
onClick={() => console.log("TODO")}>
|
onClick={() => console.log("TODO")}>
|
||||||
<Square size={16} />
|
abort
|
||||||
<span>abort</span>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -224,7 +210,6 @@ const RuntimeControls = ({ start, fast, step, running, level, code, autostep, to
|
|||||||
</div>)
|
</div>)
|
||||||
}
|
}
|
||||||
return (<div className="controlbuttons">
|
return (<div className="controlbuttons">
|
||||||
<LevelsButton />
|
|
||||||
<StartButton />
|
<StartButton />
|
||||||
<DebugButton />
|
<DebugButton />
|
||||||
</div>)
|
</div>)
|
||||||
|
|||||||
@ -15,9 +15,6 @@ import { openModal, closeModal } from "../store/gameScreenState/slice";
|
|||||||
import game from "../game/game"
|
import game from "../game/game"
|
||||||
import { setLevel, nextLevel } from "../store/level/slice";
|
import { setLevel, nextLevel } from "../store/level/slice";
|
||||||
import { RESET_EXECUTION } from "../store/executionState/types";
|
import { RESET_EXECUTION } from "../store/executionState/types";
|
||||||
import { INIT_CODEBLOCKS } from "../store/codeBlocks/types";
|
|
||||||
import store from "../store/store";
|
|
||||||
import { LayoutGrid, ArrowRight } from "lucide-react";
|
|
||||||
|
|
||||||
const MODAL_STYLES = {
|
const MODAL_STYLES = {
|
||||||
'overlay': { background: '#11111166', zIndex: 99999 },
|
'overlay': { background: '#11111166', zIndex: 99999 },
|
||||||
@ -83,16 +80,8 @@ const WonMessage = ({ nextRoute }) => {
|
|||||||
<SpeechBubble message={"Good Job!"} />
|
<SpeechBubble message={"Good Job!"} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display: "flex", justifyContent: "center", gap: "1rem", marginTop: "1rem" }}>
|
<Link to={'/levels/'}>to levels</Link>
|
||||||
<Link to={'/levels/'} className="modal-button levels">
|
<Link to={nextRoute}> next</Link>
|
||||||
<LayoutGrid size={18} />
|
|
||||||
<span>levels</span>
|
|
||||||
</Link>
|
|
||||||
<Link to={nextRoute} className="modal-button next">
|
|
||||||
<span>next</span>
|
|
||||||
<ArrowRight size={18} />
|
|
||||||
</Link>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -111,28 +100,24 @@ const GameScreen = ({ nextLevel, won, open, openModal, closeModal, setLevel, res
|
|||||||
let nextRoute = levelIdx === levels.length - 1 ? '/won' : '/level/' + levels[levelIdx + 1].name;
|
let nextRoute = levelIdx === levels.length - 1 ? '/won' : '/level/' + levels[levelIdx + 1].name;
|
||||||
|
|
||||||
let [idxMsgs, setMsgIdx] = useState(level.introMessages && level.introMessages.length > 0 ? 0 : null);
|
let [idxMsgs, setMsgIdx] = useState(level.introMessages && level.introMessages.length > 0 ? 0 : null);
|
||||||
|
console.log("GameScreen():won=", won)
|
||||||
|
|
||||||
// Auto-open modal for intro messages when level starts or when won
|
// Auto-open modal for intro messages when level starts or when won
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
// Auto-open modal for intro messages when level starts
|
// Auto-open modal for intro messages when level starts
|
||||||
if (level.introMessages && level.introMessages.length > 0 && idxMsgs !== null && !open && !won) {
|
if (level.introMessages && level.introMessages.length > 0 && idxMsgs !== null && !open && !won) {
|
||||||
openModal();
|
openModal();
|
||||||
|
console.log("GameScreen():autoopen")
|
||||||
}
|
}
|
||||||
|
|
||||||
// Auto-open modal when won
|
// Auto-open modal when won
|
||||||
if (won && !open) {
|
if (won && !open) {
|
||||||
openModal();
|
openModal();
|
||||||
|
console.log("GameScreen():autoopen-won")
|
||||||
}
|
}
|
||||||
}, [idxMsgs, won, level.introMessages]); // Proper dependencies
|
}, [idxMsgs, won, level.introMessages]); // Proper dependencies
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => game.init(level), []); // only called at creation
|
||||||
resetState();
|
|
||||||
game.init(level);
|
|
||||||
store.dispatch({ type: INIT_CODEBLOCKS, level: level });
|
|
||||||
if (!level.introMessages || level.introMessages.length === 0) {
|
|
||||||
closeModal();
|
|
||||||
}
|
|
||||||
}, [level]);
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="game" ref={modalContainerRef}>
|
<div className="game" ref={modalContainerRef}>
|
||||||
|
|||||||
@ -5,7 +5,6 @@ import { connect } from "react-redux";
|
|||||||
import { levels } from "../game/levels";
|
import { levels } from "../game/levels";
|
||||||
import { Route, Link } from "react-router-dom";
|
import { Route, Link } from "react-router-dom";
|
||||||
import LevelPreview from "./LevelPreview"
|
import LevelPreview from "./LevelPreview"
|
||||||
import { Play } from "lucide-react";
|
|
||||||
|
|
||||||
|
|
||||||
const LevelItem = ({ level, idx, cb, active, unlocked }) => {
|
const LevelItem = ({ level, idx, cb, active, unlocked }) => {
|
||||||
@ -45,11 +44,7 @@ const LevelSelectScreen = ({saveState}) =>{
|
|||||||
unlocked={unlockedState[idx]}
|
unlocked={unlockedState[idx]}
|
||||||
/>
|
/>
|
||||||
})}
|
})}
|
||||||
<Link to={'/level/' + levels[activeIdx].name} className="start-button">
|
<Link to={'/level/' + levels[activeIdx].name}>start</Link>
|
||||||
|
|
||||||
<span> start </span>
|
|
||||||
<Play size={20} />
|
|
||||||
</Link>
|
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
</div>)
|
</div>)
|
||||||
|
|||||||
@ -1,16 +1,14 @@
|
|||||||
import * as React from "react";
|
import * as React from "react";
|
||||||
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
|
import { BrowserRouter as Router, Route, Link } from "react-router-dom";
|
||||||
import { LayoutGrid, Settings } from "lucide-react";
|
|
||||||
|
|
||||||
const StartScreen = () => (
|
const StartScreen = () => (
|
||||||
<div className="startContainer start-screen">
|
<div className="startContainer">
|
||||||
<Link to="/levels/" className="controlbutton levels">
|
<img className="gameName" src="/assets/textures/gamename.png" />
|
||||||
<LayoutGrid size={18} />
|
<Link to="/levels">
|
||||||
<span>Levels</span>
|
<div className="startButton">Levels</div>
|
||||||
</Link>
|
</Link>
|
||||||
<Link to="/settings" className="controlbutton settings">
|
<Link to="/settings">
|
||||||
<Settings size={18} />
|
<div className="startButton">Settings</div>
|
||||||
<span>Settings</span>
|
|
||||||
</Link>
|
</Link>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -4,7 +4,6 @@ import * as THREE from 'three';
|
|||||||
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
import { GLTFLoader } from 'three/examples/jsm/loaders/GLTFLoader.js';
|
||||||
import { AppState } from '../store';
|
import { AppState } from '../store';
|
||||||
import { connect } from 'react-redux';
|
import { connect } from 'react-redux';
|
||||||
import { PLAYER_MODEL_FILENAME } from '../game/player';
|
|
||||||
|
|
||||||
|
|
||||||
interface TalkyFaceProps {
|
interface TalkyFaceProps {
|
||||||
@ -46,20 +45,20 @@ class TalkyFace extends Component<TalkyFaceProps> {
|
|||||||
this.scene.background = new THREE.Color(0x555555);
|
this.scene.background = new THREE.Color(0x555555);
|
||||||
|
|
||||||
// LIGHTS
|
// LIGHTS
|
||||||
this.scene.add(new THREE.HemisphereLight(0x443333, 0x111122,3));
|
this.scene.add(new THREE.HemisphereLight(0x443333, 0x111122,30));
|
||||||
|
|
||||||
let pl = new THREE.PointLight(0xFFFFFFF, 10, 0,0.3);
|
let pl = new THREE.PointLight(0xFFFFFFF, 1, 1000);
|
||||||
pl.position.set(50, -50, 10);
|
pl.position.set(50, -50, 10);
|
||||||
this.scene.add(pl);
|
this.scene.add(pl);
|
||||||
|
|
||||||
let pl1 = new THREE.PointLight(0xFFFFFFF, 10,0,0.3);
|
let pl1 = new THREE.PointLight(0xFFFFFFF, 1, 1000);
|
||||||
pl1.position.set(-50, 50, 50);
|
pl1.position.set(-50, 50, 50);
|
||||||
this.scene.add(pl1);
|
this.scene.add(pl1);
|
||||||
|
|
||||||
// ROBOT MODEL
|
// ROBOT MODEL
|
||||||
this.clock = new THREE.Clock()
|
this.clock = new THREE.Clock()
|
||||||
let loader = new GLTFLoader();
|
let loader = new GLTFLoader();
|
||||||
loader.load(PLAYER_MODEL_FILENAME, (gltf) => {
|
loader.load('assets/models/RobotExpressive.glb', (gltf) => {
|
||||||
this.model = gltf.scene;
|
this.model = gltf.scene;
|
||||||
this.animations = gltf.animations;
|
this.animations = gltf.animations;
|
||||||
this.mixer = new THREE.AnimationMixer(this.model)
|
this.mixer = new THREE.AnimationMixer(this.model)
|
||||||
|
|||||||
125
src/game/game.ts
125
src/game/game.ts
@ -84,25 +84,7 @@ class Game implements ThreeScene {
|
|||||||
|
|
||||||
init = (lev: LevelInitializer) => {
|
init = (lev: LevelInitializer) => {
|
||||||
this.scene = new THREE.Scene();
|
this.scene = new THREE.Scene();
|
||||||
|
this.scene.background = new THREE.Color(0x0a0a12); // Dark blue-gray instead of pure black
|
||||||
// Gradient background: orange → pink → purple → dark
|
|
||||||
const canvas = document.createElement('canvas');
|
|
||||||
canvas.width = 2;
|
|
||||||
canvas.height = 512;
|
|
||||||
const ctx = canvas.getContext('2d')!;
|
|
||||||
const gradient = ctx.createLinearGradient(0, 0, 0, 512);
|
|
||||||
gradient.addColorStop(0, '#0a0515'); // dark at top
|
|
||||||
gradient.addColorStop(0.4, '#2a1040'); // purple
|
|
||||||
gradient.addColorStop(0.7, '#ff4080'); // pink
|
|
||||||
gradient.addColorStop(1, '#ff6b35'); // orange at bottom
|
|
||||||
ctx.fillStyle = gradient;
|
|
||||||
ctx.fillRect(0, 0, 2, 512);
|
|
||||||
const bgTexture = new THREE.CanvasTexture(canvas);
|
|
||||||
bgTexture.magFilter = THREE.LinearFilter;
|
|
||||||
this.scene.background = bgTexture;
|
|
||||||
|
|
||||||
// Fog to blend distant terrain into sky
|
|
||||||
this.scene.fog = new THREE.FogExp2(0x2a1040, 0.008);
|
|
||||||
|
|
||||||
// Groundplane
|
// Groundplane
|
||||||
initGround(this.scene);
|
initGround(this.scene);
|
||||||
@ -115,27 +97,21 @@ class Game implements ThreeScene {
|
|||||||
this.player = new Player(this.scene, { ...level, voxels: mutableVoxels })
|
this.player = new Player(this.scene, { ...level, voxels: mutableVoxels })
|
||||||
initBoxes(this.scene, mutableVoxels, this.player);
|
initBoxes(this.scene, mutableVoxels, this.player);
|
||||||
|
|
||||||
// Lights - Synthwave sunset style
|
// Lights
|
||||||
// Subtle ambient light for base visibility
|
// Subtle ambient light for base visibility
|
||||||
this.scene.add(new THREE.AmbientLight(0x3a1a4a, 2.4));
|
this.scene.add(new THREE.AmbientLight(0x2a2a3a, 2.4));
|
||||||
|
|
||||||
// Hemisphere light: orange sky, purple ground
|
// Improved hemisphere light for mood
|
||||||
this.scene.add(new THREE.HemisphereLight(0xff6b35, 0x2a1040, 2));
|
this.scene.add(new THREE.HemisphereLight(0x4a4a5a, 0x1a1a2a, 10));
|
||||||
|
|
||||||
// Dramatic point lights with warm pink tones
|
// Dramatic point lights with better positioning
|
||||||
let pl = new THREE.PointLight(0xffffff, 10, 0,0.3);
|
let pl = new THREE.PointLight(0x8090ff, 10, 0,0.3);
|
||||||
pl.position.set(40, -40, 25);
|
pl.position.set(40, -40, 25);
|
||||||
pl.castShadow = true;
|
pl.castShadow = true;
|
||||||
pl.shadow.mapSize.width = 2048;
|
|
||||||
pl.shadow.mapSize.height = 2048;
|
|
||||||
pl.shadow.bias = -0.001;
|
|
||||||
this.scene.add(pl);
|
this.scene.add(pl);
|
||||||
let pl1 = new THREE.PointLight(0xffffff, 10, 0,0.3);
|
let pl1 = new THREE.PointLight(0xff9080, 10, 0,0.3);
|
||||||
pl1.position.set(-40, 40, 25);
|
pl1.position.set(-40, 40, 25);
|
||||||
pl1.castShadow = true;
|
pl1.castShadow = true;
|
||||||
pl1.shadow.mapSize.width = 2048;
|
|
||||||
pl1.shadow.mapSize.height = 2048;
|
|
||||||
pl1.shadow.bias = -0.001;
|
|
||||||
this.scene.add(pl1);
|
this.scene.add(pl1);
|
||||||
|
|
||||||
}
|
}
|
||||||
@ -232,10 +208,6 @@ function initBoxes(scene: THREE.Scene, voxels: Array<Voxel>, player: Player) {
|
|||||||
mesh.castShadow = true;
|
mesh.castShadow = true;
|
||||||
mesh.receiveShadow = true;
|
mesh.receiveShadow = true;
|
||||||
mesh.position.set(x, y, z);
|
mesh.position.set(x, y, z);
|
||||||
|
|
||||||
const edges = new THREE.EdgesGeometry(geometry);
|
|
||||||
const line = new THREE.LineSegments(edges, new THREE.LineBasicMaterial({ color: 0x000000, linewidth: 2 }));
|
|
||||||
mesh.add(line);
|
|
||||||
console.log(player.voxelStandingOn)
|
console.log(player.voxelStandingOn)
|
||||||
if (idx != player.level.playerPos.voxelIdx) {
|
if (idx != player.level.playerPos.voxelIdx) {
|
||||||
mesh.position.setZ(10)
|
mesh.position.setZ(10)
|
||||||
@ -271,84 +243,69 @@ function initGround(scene: THREE.Scene) {
|
|||||||
var data = generateHeight(128, 128);
|
var data = generateHeight(128, 128);
|
||||||
let vertices = planeGeometry.attributes.position.array;
|
let vertices = planeGeometry.attributes.position.array;
|
||||||
|
|
||||||
|
|
||||||
|
console.log(vertices.length)
|
||||||
for (var i = 0, j = 0, l = vertices.length; i < l; i++ , j += 3) {
|
for (var i = 0, j = 0, l = vertices.length; i < l; i++ , j += 3) {
|
||||||
|
// console.log(vertices[j + 1])
|
||||||
(vertices[j + 1] as number) = data[i];
|
(vertices[j + 1] as number) = data[i];
|
||||||
}
|
}
|
||||||
planeGeometry.computeVertexNormals();
|
|
||||||
|
|
||||||
var planeWire = new THREE.Mesh(planeGeometry,
|
var planeWire = new THREE.Mesh(planeGeometry,
|
||||||
|
|
||||||
new THREE.MeshPhongMaterial({
|
new THREE.MeshPhongMaterial({
|
||||||
color: 0xff00ff,
|
color: 0x320032,
|
||||||
specular: 0x0,
|
specular: 0x0,
|
||||||
wireframeLinewidth: 2,
|
// shininess: 0xffffff,
|
||||||
|
wireframeLinewidth: 3,
|
||||||
wireframe: true
|
wireframe: true
|
||||||
|
|
||||||
|
})
|
||||||
|
);
|
||||||
|
var planeSolid = new THREE.Mesh(planeGeometry,
|
||||||
|
|
||||||
|
new THREE.MeshPhongMaterial({
|
||||||
|
color: 0x030211,
|
||||||
|
specular: 0x0,
|
||||||
|
shininess: 0x0,
|
||||||
|
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
planeWire.position.y = 0;
|
planeWire.position.y = 0;
|
||||||
planeWire.rotateX(Math.PI * 0.5)
|
planeWire.rotateX(Math.PI * 0.5)
|
||||||
|
|
||||||
var planeSolid = new THREE.Mesh(planeGeometry,
|
|
||||||
new THREE.MeshPhongMaterial({
|
|
||||||
color: 0x0a0510,
|
|
||||||
specular: 0x0,
|
|
||||||
shininess: 0x0,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
planeSolid.position.y = 0;
|
planeSolid.position.y = 0;
|
||||||
planeSolid.rotateX(Math.PI * 0.5)
|
planeSolid.rotateX(Math.PI * 0.5)
|
||||||
planeSolid.receiveShadow = true;
|
planeSolid.receiveShadow = true;
|
||||||
|
|
||||||
var grid = new THREE.GridHelper(200, 20, 0xff00ff, 0xff00ff);
|
|
||||||
|
var grid = new THREE.GridHelper(400, 30, 0x99bbff, 0x99bbff);
|
||||||
grid.rotateX(Math.PI * 0.5);
|
grid.rotateX(Math.PI * 0.5);
|
||||||
grid.position.z = 0.05;
|
// grid.position.setZ(0.5);
|
||||||
|
// console.log(grid.material)
|
||||||
(grid.material as THREE.LineBasicMaterial).opacity = 0.2;
|
(grid.material as THREE.LineBasicMaterial).opacity = 0.2;
|
||||||
(grid.material as THREE.LineBasicMaterial).transparent = true;
|
(grid.material as THREE.LineBasicMaterial).transparent = true;
|
||||||
|
|
||||||
var gridFar = new THREE.GridHelper(200, 40, 0xff00ff, 0xff00ff);
|
// scene.add(planeWire);
|
||||||
gridFar.rotateX(Math.PI * 0.5);
|
// scene.add(planeSolid);
|
||||||
gridFar.position.z = 0.02;
|
scene.add(grid);
|
||||||
(gridFar.material as THREE.LineBasicMaterial).opacity = 0.1;
|
|
||||||
(gridFar.material as THREE.LineBasicMaterial).transparent = true;
|
|
||||||
|
|
||||||
scene.add(planeWire);
|
|
||||||
scene.add(planeSolid);
|
|
||||||
//scene.add(gridFar);
|
|
||||||
//scene.add(grid);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// used to generate terrain
|
||||||
function generateHeight(width, height) {
|
function generateHeight(width, height) {
|
||||||
var size = width * height, data = new Uint8Array(size),
|
var size = width * height, data = new Uint8Array(size),
|
||||||
perlin = new ImprovedNoise(), quality = 1, z = Math.random() * 20;
|
perlin = new ImprovedNoise(), quality = 1, z = Math.random() * 200;
|
||||||
|
|
||||||
for (var j = 0; j < 4; j++) {
|
for (var j = 0; j < 4; j++) {
|
||||||
for (var i = 0; i < size; i++) {
|
for (var i = 0; i < size; i++) {
|
||||||
var ix = i % width, iy = ~ ~(i / width);
|
var x = i % width, y = ~ ~(i / width);
|
||||||
data[i] += Math.abs(perlin.noise(ix / quality, iy / quality, z) * quality * 1.75);
|
data[i] += Math.abs(perlin.noise(x / quality, y / quality, z) * quality * 1.75);
|
||||||
|
|
||||||
}
|
}
|
||||||
quality *= 5;
|
quality *= 5;
|
||||||
}
|
}
|
||||||
|
for (var j = 0; j < size; j++) {
|
||||||
for (var i = 0; i < size; i++) {
|
var x = ((j % width) - 64)
|
||||||
var x = ((i % width) - 64);
|
var y = ((~ ~(j / width)) - 64)
|
||||||
var y = ((~ ~(i / width)) - 64);
|
data[j] *= Math.sqrt(x * x + y * y) * 0.005
|
||||||
var distFromCenter = Math.sqrt(x * x + y * y);
|
|
||||||
|
|
||||||
var flatRadius = 4;
|
|
||||||
var mountainStart = 50;
|
|
||||||
|
|
||||||
if (distFromCenter < flatRadius) {
|
|
||||||
data[i] = 0;
|
|
||||||
} else if (distFromCenter < mountainStart) {
|
|
||||||
var t = (distFromCenter - flatRadius) / (mountainStart - flatRadius);
|
|
||||||
var rise = Math.pow(t, 1.5);
|
|
||||||
data[i] = data[i] * rise * 1.5;
|
|
||||||
} else {
|
|
||||||
var valleyWidth = 25;
|
|
||||||
var valleyDepth = Math.max(0, 1 - Math.abs(x) / valleyWidth);
|
|
||||||
valleyDepth = Math.pow(valleyDepth, 2);
|
|
||||||
data[i] = data[i] * (2.5 - valleyDepth * 1.2);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return data;
|
return data;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -45,7 +45,7 @@ export const levels: LevelInitializer[] = [
|
|||||||
{ name: "action", containerIndex: 0 }]
|
{ name: "action", containerIndex: 0 }]
|
||||||
}],
|
}],
|
||||||
activeCodeview: "main",
|
activeCodeview: "main",
|
||||||
allowedCodeBlocks: ["forward", "turn_right", "turn_left", "jump", "action"],
|
allowedCodeBlocks: ["forward", "turn_right", "turn_left", "jump", "action", "f1"],
|
||||||
introMessages: [{ message: "Hit run and watch.", image: null },
|
introMessages: [{ message: "Hit run and watch.", image: null },
|
||||||
{ message: "second page of intro message", image: null }]
|
{ message: "second page of intro message", image: null }]
|
||||||
},
|
},
|
||||||
@ -59,25 +59,6 @@ export const levels: LevelInitializer[] = [
|
|||||||
{ x: 3, y: 2, z: 0 },
|
{ x: 3, y: 2, z: 0 },
|
||||||
{ x: 3, y: 3, z: 0, actionable: { type: BUTTON_ACTIONABLE.type, pushed: BUTTON_ACTIONABLE.pushed } }],
|
{ x: 3, y: 3, z: 0, actionable: { type: BUTTON_ACTIONABLE.type, pushed: BUTTON_ACTIONABLE.pushed } }],
|
||||||
playerPos: { voxelIdx: 0, direction: "east" },
|
playerPos: { voxelIdx: 0, direction: "east" },
|
||||||
codeviews: [{
|
|
||||||
name: "main",
|
|
||||||
nMaxBlocks: 9,
|
|
||||||
blocks: [{ name: "forward" }, { name: "forward" }]
|
|
||||||
}],
|
|
||||||
activeCodeview: "main",
|
|
||||||
allowedCodeBlocks: ["forward", "turn_right", "turn_left", "jump", "action"],
|
|
||||||
introMessages: [{ message: "light up all dark cubes!", image: null }]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: "hello fn",
|
|
||||||
voxels: [{ x: 0, y: 0, z: 0 },
|
|
||||||
{ x: 1, y: 0, z: 0 },
|
|
||||||
{ x: 2, y: 0, z: 0 },
|
|
||||||
{ x: 3, y: 0, z: 0, actionable: { type: BUTTON_ACTIONABLE.type, pushed: BUTTON_ACTIONABLE.pushed } },
|
|
||||||
{ x: 3, y: 1, z: 0 },
|
|
||||||
{ x: 3, y: 2, z: 0 },
|
|
||||||
{ x: 3, y: 3, z: 0, actionable: { type: BUTTON_ACTIONABLE.type, pushed: BUTTON_ACTIONABLE.pushed } }],
|
|
||||||
playerPos: { voxelIdx: 0, direction: "east" },
|
|
||||||
codeviews: [{
|
codeviews: [{
|
||||||
name: "main",
|
name: "main",
|
||||||
nMaxBlocks: 3,
|
nMaxBlocks: 3,
|
||||||
@ -91,17 +72,16 @@ export const levels: LevelInitializer[] = [
|
|||||||
}],
|
}],
|
||||||
activeCodeview: "main",
|
activeCodeview: "main",
|
||||||
allowedCodeBlocks: ["forward", "turn_right", "turn_left", "jump", "action", "f1"],
|
allowedCodeBlocks: ["forward", "turn_right", "turn_left", "jump", "action", "f1"],
|
||||||
introMessages: [{ message: "Use a function for repeating patterns!", image: null }]
|
introMessages: [{ message: "light up all dark cubes!", image: null }]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "jump n run",
|
name: "jump n run",
|
||||||
voxels: [{ x: 0, y: 0, z: 0},
|
voxels: [{ x: 0, y: 0, z: 0, actionable: { type: BUTTON_ACTIONABLE.type, pushed: BUTTON_ACTIONABLE.pushed } },
|
||||||
{ x: 0, y: 1, z: 1 },
|
{ x: 0, y: 1, z: 1 },
|
||||||
{ x: 0, y: 2, z: 1 , actionable: { type: BUTTON_ACTIONABLE.type, pushed: BUTTON_ACTIONABLE.pushed } },
|
{ x: 0, y: 2, z: 1 },
|
||||||
{ x: 0, y: 3, z: 2 },
|
{ x: 0, y: 3, z: 0 },
|
||||||
{ x: 0, y: 4, z: 2 , actionable: { type: BUTTON_ACTIONABLE.type, pushed: BUTTON_ACTIONABLE.pushed } },
|
{ x: 0, y: 4, z: 0 },
|
||||||
{ x: 0, y: 5, z: 3},
|
{ x: 0, y: 5, z: 0, actionable: { type: BUTTON_ACTIONABLE.type, pushed: BUTTON_ACTIONABLE.pushed } }],
|
||||||
{ x: 0, y: 6, z: 3, actionable: { type: BUTTON_ACTIONABLE.type, pushed: BUTTON_ACTIONABLE.pushed } }],
|
|
||||||
playerPos: { voxelIdx: 0, direction: "north" },
|
playerPos: { voxelIdx: 0, direction: "north" },
|
||||||
codeviews: [{ name: "main", nMaxBlocks: 4, blocks: [] },
|
codeviews: [{ name: "main", nMaxBlocks: 4, blocks: [] },
|
||||||
{ name: "f1", nMaxBlocks: 3, blocks: [] }],
|
{ name: "f1", nMaxBlocks: 3, blocks: [] }],
|
||||||
@ -111,16 +91,15 @@ export const levels: LevelInitializer[] = [
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: "jump n run 2",
|
name: "jump n run 2",
|
||||||
voxels: [{ x: 0, y: 0, z: 0},
|
voxels: [{ x: 0, y: 0, z: 0, actionable: { type: BUTTON_ACTIONABLE.type, pushed: BUTTON_ACTIONABLE.pushed } },
|
||||||
{ x: 0, y: 1, z: 1 },
|
{ x: 0, y: 1, z: 1 },
|
||||||
{ x: 0, y: 2, z: 1 , actionable: { type: BUTTON_ACTIONABLE.type, pushed: BUTTON_ACTIONABLE.pushed } },
|
{ x: 0, y: 2, z: 1 },
|
||||||
{ x: 1, y: 2, z: 2 },
|
{ x: 1, y: 2, z: 0 },
|
||||||
{ x: 2, y: 2, z: 2 , actionable: { type: BUTTON_ACTIONABLE.type, pushed: BUTTON_ACTIONABLE.pushed } },
|
{ x: 2, y: 2, z: 0 },
|
||||||
{ x: 2, y: 1, z: 3},
|
{ x: 3, y: 2, z: 0, actionable: { type: BUTTON_ACTIONABLE.type, pushed: BUTTON_ACTIONABLE.pushed } }],
|
||||||
{ x: 2, y: 0, z: 3, actionable: { type: BUTTON_ACTIONABLE.type, pushed: BUTTON_ACTIONABLE.pushed }}],
|
|
||||||
playerPos: { voxelIdx: 0, direction: "north" },
|
playerPos: { voxelIdx: 0, direction: "north" },
|
||||||
codeviews: [{ name: "main", nMaxBlocks: 1, blocks: [] },
|
codeviews: [{ name: "main", nMaxBlocks: 4, blocks: [] },
|
||||||
{ name: "f1", nMaxBlocks: 5, blocks: [] }],
|
{ name: "f1", nMaxBlocks: 3, blocks: [] }],
|
||||||
activeCodeview: "main",
|
activeCodeview: "main",
|
||||||
allowedCodeBlocks: ["forward", "turn_right", "turn_left", "jump", "action", "f1"],
|
allowedCodeBlocks: ["forward", "turn_right", "turn_left", "jump", "action", "f1"],
|
||||||
introMessages: null
|
introMessages: null
|
||||||
|
|||||||
@ -13,8 +13,7 @@ import { wonLevel } from '../store/executionState/actions';
|
|||||||
// model control from https://github.com/mrdoob/three.js/blob/master/examples/webgl_animation_skinning_morph.html
|
// model control from https://github.com/mrdoob/three.js/blob/master/examples/webgl_animation_skinning_morph.html
|
||||||
// var states = ['Idle', 'Walking', 'Running', 'Dance', 'Death', 'Sitting', 'Standing'];
|
// var states = ['Idle', 'Walking', 'Running', 'Dance', 'Death', 'Sitting', 'Standing'];
|
||||||
// var emotes = ['Jump', 'Yes', 'No', 'Wave', 'Punch', 'ThumbsUp'];
|
// var emotes = ['Jump', 'Yes', 'No', 'Wave', 'Punch', 'ThumbsUp'];
|
||||||
export const PLAYER_MODEL_FILENAME = '/assets/models/RobotExpressive.glb'
|
|
||||||
//export const PLAYER_MODEL_FILENAME = '/assets/models/robot.glb'
|
|
||||||
export class Player {
|
export class Player {
|
||||||
scene: THREE.Scene;
|
scene: THREE.Scene;
|
||||||
model: THREE.Object3D;
|
model: THREE.Object3D;
|
||||||
@ -33,7 +32,7 @@ export class Player {
|
|||||||
this.level = level;
|
this.level = level;
|
||||||
this.clock = new THREE.Clock()
|
this.clock = new THREE.Clock()
|
||||||
let loader = new GLTFLoader();
|
let loader = new GLTFLoader();
|
||||||
loader.load(PLAYER_MODEL_FILENAME, (gltf) => {
|
loader.load('assets/models/RobotExpressive.glb', (gltf) => {
|
||||||
this.model = gltf.scene;
|
this.model = gltf.scene;
|
||||||
this.animations = gltf.animations;
|
this.animations = gltf.animations;
|
||||||
this.mixer = new THREE.AnimationMixer(this.model)
|
this.mixer = new THREE.AnimationMixer(this.model)
|
||||||
@ -70,11 +69,11 @@ export class Player {
|
|||||||
}
|
}
|
||||||
|
|
||||||
reset() {
|
reset() {
|
||||||
this.voxels = resetVoxels(this.voxels)
|
|
||||||
this.voxelStandingOn = this.voxels[this.level.playerPos.voxelIdx]
|
this.voxelStandingOn = this.voxels[this.level.playerPos.voxelIdx]
|
||||||
this.setToVoxelPosition(this.voxelStandingOn)
|
this.setToVoxelPosition(this.voxelStandingOn)
|
||||||
this.direction = this.level.playerPos.direction
|
this.direction = this.level.playerPos.direction
|
||||||
this.initRotation(this.direction)
|
this.initRotation(this.direction)
|
||||||
|
resetVoxels(this.voxels)
|
||||||
}
|
}
|
||||||
|
|
||||||
doneAnimating() {
|
doneAnimating() {
|
||||||
|
|||||||
187
src/index.css
187
src/index.css
@ -1,4 +1,5 @@
|
|||||||
body {
|
body {
|
||||||
|
background:#223;
|
||||||
}
|
}
|
||||||
|
|
||||||
* {
|
* {
|
||||||
@ -92,12 +93,11 @@ h1 {
|
|||||||
width:16rem;
|
width:16rem;
|
||||||
margin-bottom:0.8rem;
|
margin-bottom:0.8rem;
|
||||||
height:12rem;
|
height:12rem;
|
||||||
background:#2D2A2E;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.fncontainer h2 {
|
.fncontainer h3 {
|
||||||
text-indent:-2rem;
|
text-indent:-2rem;
|
||||||
color:#AB9DF2;
|
color:#9bf;
|
||||||
display:block;
|
display:block;
|
||||||
margin-bottom:0.5rem;
|
margin-bottom:0.5rem;
|
||||||
margin-top:0.4rem;
|
margin-top:0.4rem;
|
||||||
@ -116,8 +116,8 @@ h1 {
|
|||||||
|
|
||||||
.op {
|
.op {
|
||||||
list-style-type:none;
|
list-style-type:none;
|
||||||
background:#3E3D32;
|
background:#444;
|
||||||
border: 0.1rem solid #49483E;
|
border: 0.1rem solid black;
|
||||||
border-radius: 0.2rem;
|
border-radius: 0.2rem;
|
||||||
width:3.8rem;
|
width:3.8rem;
|
||||||
height:3.8rem;
|
height:3.8rem;
|
||||||
@ -131,7 +131,7 @@ h1 {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.opspalette {
|
.opspalette {
|
||||||
background:#1E1F1C;
|
background:#122;
|
||||||
display:flex;
|
display:flex;
|
||||||
flex-flow: row;
|
flex-flow: row;
|
||||||
padding-top: 1.6rem;
|
padding-top: 1.6rem;
|
||||||
@ -144,15 +144,14 @@ h1 {
|
|||||||
display:flex;
|
display:flex;
|
||||||
flex-flow: row wrap;
|
flex-flow: row wrap;
|
||||||
position:absolute;
|
position:absolute;
|
||||||
z-index:0;
|
z-index:-2;
|
||||||
width:16rem;
|
width:16rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.opindicator {
|
.opindicator {
|
||||||
width:4rem;
|
width:4rem;
|
||||||
height:4rem;
|
height:4rem;
|
||||||
background:#444640;
|
background:#555;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.opdropzones {
|
.opdropzones {
|
||||||
@ -165,6 +164,8 @@ h1 {
|
|||||||
.opdropzone {
|
.opdropzone {
|
||||||
width:4rem;
|
width:4rem;
|
||||||
height:4rem;
|
height:4rem;
|
||||||
|
|
||||||
|
background:#1119;
|
||||||
}
|
}
|
||||||
|
|
||||||
.frontdropper {
|
.frontdropper {
|
||||||
@ -176,12 +177,12 @@ h1 {
|
|||||||
margin-top:1rem;
|
margin-top:1rem;
|
||||||
margin-bottom:0.4rem;
|
margin-bottom:0.4rem;
|
||||||
margin-left:2rem;
|
margin-left:2rem;
|
||||||
color:#FCFCFA;
|
color:#666;
|
||||||
user-select:none;
|
user-select:none;
|
||||||
}
|
}
|
||||||
|
|
||||||
.titlebar{
|
.titlebar{
|
||||||
background:#1E1F1C;
|
background:#122;
|
||||||
width:100%;
|
width:100%;
|
||||||
display:flex;
|
display:flex;
|
||||||
flex-direction:row;
|
flex-direction:row;
|
||||||
@ -203,8 +204,8 @@ h1 {
|
|||||||
width:100%;
|
width:100%;
|
||||||
height:100vh;
|
height:100vh;
|
||||||
font-family:iosevka;
|
font-family:iosevka;
|
||||||
color:#FCFCFA;
|
color:#fff;
|
||||||
background:#2D2A2E;
|
border-left: solid #112 0.4rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.controlbuttons {
|
.controlbuttons {
|
||||||
@ -213,118 +214,20 @@ h1 {
|
|||||||
margin-right:2rem;
|
margin-right:2rem;
|
||||||
margin-top:0.4rem;
|
margin-top:0.4rem;
|
||||||
height:2.4rem;
|
height:2.4rem;
|
||||||
gap: 0.5rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.controlbutton {
|
.controlbutton {
|
||||||
padding: 0.7rem 1.4rem;
|
padding:0.5rem;
|
||||||
display: flex;
|
display:block;
|
||||||
align-items: center;
|
border: solid #112 0.05rem;
|
||||||
gap: 0.5rem;
|
border-radius:0.3rem;
|
||||||
border: none;
|
background:#221;
|
||||||
border-radius: 0.5rem;
|
|
||||||
cursor:pointer;
|
cursor:pointer;
|
||||||
user-select:none;
|
user-select:none;
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 500;
|
|
||||||
transition: all 0.15s ease;
|
|
||||||
color: #fff;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.controlbutton span {
|
.controlbutton:hover {
|
||||||
line-height: 1;
|
background:#332;
|
||||||
}
|
|
||||||
|
|
||||||
.controlbutton.run {
|
|
||||||
background: #2a7;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controlbutton.run:hover {
|
|
||||||
background: #3b8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controlbutton.levels {
|
|
||||||
background: #48b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controlbutton.levels:hover {
|
|
||||||
background: #59c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controlbutton.settings {
|
|
||||||
background: #636;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controlbutton.settings:hover {
|
|
||||||
background: #747;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controlbutton.debug {
|
|
||||||
background: #eA5;
|
|
||||||
color: #222;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controlbutton.debug:hover {
|
|
||||||
background: #fb6;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controlbutton.step {
|
|
||||||
background: #48b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controlbutton.step:hover {
|
|
||||||
background: #59c;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controlbutton.reset {
|
|
||||||
background: #4af;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controlbutton.reset:hover {
|
|
||||||
background: #5bf;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controlbutton.speed {
|
|
||||||
background: #636;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controlbutton.speed:hover {
|
|
||||||
background: #747;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controlbutton.abort {
|
|
||||||
background: #e55;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controlbutton.abort:hover {
|
|
||||||
background: #f66;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-button {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
padding: 0.6rem 1.2rem;
|
|
||||||
border: none;
|
|
||||||
border-radius: 0.4rem;
|
|
||||||
font-size: 1rem;
|
|
||||||
font-weight: 500;
|
|
||||||
color: #fff;
|
|
||||||
text-decoration: none;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.15s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-button:hover {
|
|
||||||
filter: brightness(1.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-button.levels {
|
|
||||||
background: #48b;
|
|
||||||
}
|
|
||||||
|
|
||||||
.modal-button.next {
|
|
||||||
background: #2a7;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.levelSelectCaption {
|
.levelSelectCaption {
|
||||||
@ -381,53 +284,19 @@ h1 {
|
|||||||
margin:auto;
|
margin:auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
.start-screen {
|
|
||||||
background: url('/assets/textures/startscreen.png') no-repeat center center fixed;
|
|
||||||
background-size: cover;
|
|
||||||
}
|
|
||||||
|
|
||||||
.startContainer {
|
.startContainer {
|
||||||
display: flex;
|
display:flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
align-items: flex-end;
|
width: 850px;
|
||||||
justify-content: flex-end;
|
height:600px;
|
||||||
height: 80vh;
|
margin:auto;
|
||||||
padding-right: 30%;
|
justify-content: flex-start;
|
||||||
padding-bottom: 20%;
|
|
||||||
gap: 3rem;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.startContainer a {
|
.startContainer a {
|
||||||
margin-top: 0;
|
margin-top:2rem;
|
||||||
}
|
}
|
||||||
|
|
||||||
.gameName {
|
.gameName {
|
||||||
margin-top: 4rem;
|
margin-top:4rem;
|
||||||
}
|
|
||||||
|
|
||||||
.start-button {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
gap: 0.5rem;
|
|
||||||
margin: 1.5rem auto 0;
|
|
||||||
padding: 0.7rem 2rem;
|
|
||||||
background: #2a7;
|
|
||||||
border: none;
|
|
||||||
border-radius: 0.4rem;
|
|
||||||
font-size: 1.1rem;
|
|
||||||
font-weight: 600;
|
|
||||||
color: #fff;
|
|
||||||
text-decoration: none;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: all 0.15s ease;
|
|
||||||
width:auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
.start-button:hover {
|
|
||||||
background: #3b8;
|
|
||||||
}
|
|
||||||
|
|
||||||
.start-button span {
|
|
||||||
line-height: 1;
|
|
||||||
}
|
}
|
||||||
|
|||||||
@ -5,7 +5,7 @@ import { cloneDeep, pullAt } from "lodash"
|
|||||||
export function codeBlocksReducer(state: Array<CodeBlockContainer> = [], action: CodeBlocksActionTypes): Array<CodeBlockContainer> {
|
export function codeBlocksReducer(state: Array<CodeBlockContainer> = [], action: CodeBlocksActionTypes): Array<CodeBlockContainer> {
|
||||||
switch (action.type) {
|
switch (action.type) {
|
||||||
case INIT_CODEBLOCKS:
|
case INIT_CODEBLOCKS:
|
||||||
return cloneDeep(action.level.codeviews)
|
return Object.assign([], state, action.level.codeviews)
|
||||||
case REMOVE_CODEBLOCK:
|
case REMOVE_CODEBLOCK:
|
||||||
return reduceRemoveBlock(state, action)
|
return reduceRemoveBlock(state, action)
|
||||||
case INSERT_CODEBLOCK:
|
case INSERT_CODEBLOCK:
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user